程序员应如何理解标准库

Posted 程序员大咖

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了程序员应如何理解标准库相关的知识,希望对你有一定的参考价值。

????????关注后回复 “进群” ,拉你进程序员交流群????????

作者丨码农的荒岛求生

来源丨码农的荒岛求生

记得当年在学了C/C++语言后一直有这样的疑惑,常用的printf函数以及C++中的cout函数到底是在哪里实现的

相信不止我有这个疑问,这篇文章就来回答这个问题。

C/C++语言是怎样实现的

相信有的同学一定觉得编程语言是十分神秘的,实际上不是这样的。

一门编程语言的本质是什么?

本质上一门语言不过就是一堆规则(rules)而已,就像汉语中的主谓宾一样,就像

  • if之后必须是一个括号(),这个括号中必须是一个bool表达式

  • while之后必须是一个括号(),这个括号中必须是一个bool表达式

  • continue语言必须出现在while语句中

  • 等等

有的同学可能会问,为什么一定要有这堆规则呢?原来,只有有了规则之后编译器才能知道该怎么处理我们写的程序。

编译器在遇到if后就知道,接下来紧跟的一定是一个左括号,之后一定是一个bool表达式,再之后一定是一个右括号。

如果我们写的程序不满足这样的规则,结果就是编译器开始抱怨编译错误(compile error)。

让我们回到主题,实际上C/C++以及任何一门编程语言都是这样的一堆规则,对于C/C++来说,每年都有一群来自被称为International Organization for Standardization (ISO)组织的人来制定C/C++语言的规则,因此这群人坐下来讨论的这堆规则实际上就是一个标准,每一次讨论都会重新修改制定新的标准并对外发布,这就是为什么C/C++有各种版本:C99, C11, C++03, C++11, C++14等等,其中的数字其实就是来自制定标准的年份。

对外发布的标准中包含两部分内容:

  1. C/C++支持哪些特性

  2. C/C++API,程序员可以在他们的C/C++程序中直接调用这些API,这些API就被称为标准库(Standard Library)

注意发布的标准中只定义了API,但是并不包括实现,肯定有同学会问,那么是谁来实现标准中定义的API呢?

C/C++标准库的实现

至此,我们终于可以开始讨论标准库的实现问题了,实际上专门有一群人负责根据发布的API来实现标准库,程序员在实现除了一些比如数学计算之外,像文件读写、内存分配、线程创建等等相关的API的实现,这些程序员必须借助相应操作系统提供的功能,那么这些程序是怎样使用操作系统提供的功能的呢?答案就是借助系统调用(System Call),注意,很多同学可能意识不到这一点,但是这一点相当重要,那就是我们所写的代码有很多是需要依赖操作系统的,操作系统其实提供了很多功能,程序员使用这些功能的方式其实就是借助系统调用(关于系统调用,博主在《操作系统:以程序员的角度》其中"程序员应如何理解系统调用"一节中有详细的讲解)。

因此我们知道,其实每一个平台(操作系统)上都有自己特定的标准库实现,因为不同的操作系统提供的功能是不同的,因此提供的系统调用也是不同的。

现在我们就可以回答最开始提出的问题了,原来printf和cout等等的代码是实现在标准库中,那么这些标准库在哪里呢,我们的程序又是怎么用到标准库的呢?

标准库在哪里?怎样使用?

让我们用C语言写一个简单的Hello World程序:

#include <stdio.h>


int main() {
   printf("hello world\\n");
   return 0;                                     
}

然后编译、执行:

$ gcc helloworld.c -o hw
$ ./hw
hello world

我们可以看到程序正确运行了,但是问题来了,既然我们已经知道了printf其实是实现在了标准库中,那么这个过程中哪里涉及到标准库了?

要回答这个问题,我们需要知道编译可执行程序中的一个过程:链接,关于链接博主在《彻底理解链接器》系列文章中有详解的讲解。简单来说,链接的作用就是把程序依赖的各个库打包起来。要想看到可执行程序依赖哪些库,我们借助一个叫ldd的工具:

$ ldd hw
    linux-vdso.so.1 =>  (0x00007ffe075d3000)
    libc.so.6 => /usr/lib64/libc.so.6 (0x00007fcd58b75000)
    /lib64/ld-linux-x86-64.so.2 (0x000055c5dbea4000)

我们注意到可执行程序hw依赖一个叫做libc.so.6的库,位于/usr/lib64/libc.so.6,这个libc.so.6就是我们苦苦寻找的标准库

Linux中以.so结尾的文件被称为动态链接库,难怪我们看不到标准库的实现,原来都被实现好打包到了动态链接库中,关于动态链接库详见《彻底理解链接器》中第三篇,简单的说动态库中包含的就是已经被编译好的二进制程序,只不过和可执行程序相比我们无法直接运行动态库。

现在我们知道了标准库是什么,以及在哪里,有的同学可能会问,那么我们是怎么用标准库的呢?

原来,编译器gcc在编译程序是默认情况下就自动链接了标准库,因为大家写程序免不了使用标准库提供的API,因此gcc等编译器自动把标准库打包到了可执行程序中。

现在你应该明白了吧。

接下来我们就看看各个平台下标准库的实现。

Linux标准库实现

Linux下标准库的实现被称为GNU C Library,也被称为glibc,这个名字肯定有同学听过。

glibc是Linux平台中使用最为广泛的,然而有一段时间Linux发行版中的标准库多使用Libc,在经过了数年的开发后glibc又开始优于了Libc,Linux发行版又开始转回了glibc,现在在Linux发行版上你会看到磁盘上有一个libc.so.6的文件,这个文件其实就是现代版的glibc,只不过名字遵从了Linux发行版的习惯。

关于C++的标准库实现在了 libstdc++,你在Linux平台中使用ldd工具就能看到这个标准库。

Windows标准库实现

Windows标准库实现是和微软的官方编译器Visual Studio绑定在一起的,该标准库曾被称为C/C++ Run-time Library (CRT)

从Windows95开始,微软以MSVCRT+版本号.DLL的命名方式来发布,到了1997年,将其简化为了MSVCRT.DLL。

从Visual Studio 2015之后,Windows中C/C++标准库被称为Universal C Runtime Library (Universal CRT,简称UCRT),即UCRTBASE.DLL,此后Windows标准库开始同Win10一起发布。

总结

一个看似简单的问题实际上往往并不那么简单,在这篇文章中,我们从一个简单的问题开始不断挖掘背后涉及到的方方面面,希望这篇文章能帮你彻底理解标准库。

-End-

最近有一些小伙伴,让我帮忙找一些 面试题 资料,于是我翻遍了收藏的 5T 资料后,汇总整理出来,可以说是程序员面试必备!所有资料都整理到网盘了,欢迎下载!

点击????卡片,关注后回复【面试题】即可获取

在看点这里好文分享给更多人↓↓

以上是关于程序员应如何理解标准库的主要内容,如果未能解决你的问题,请参考以下文章

每个优秀程序员都应遵循的代码原则和规范

如何理解这段代码片段中的两对括号?

STM32 固件库与 CMSIS 标准如何理解?

C++ 标准库如何链接到我的应用程序?

DLL的相关理解

如何使用模块化代码片段中的LeakCanary检测内存泄漏?