要使程序能够被成功编译链接,我们需要为被调用的函数提供具体的实现。你也可能留意到了,我们一直使用的printf函数,只需包含对应的头文件,便可以保证程序链接的正确性。这是为什么呢?让我们根据现有的经验推测一下。
编写函数时,我们会先写下函数声明,然后在当前文件中实现函数定义。目前我们对函数的编写与使用都在同一文件中,而使用printf函数时,需要包含stdio.h,这表明我们有可能在此文件中找到printf函数的实现。
现在尝试打开一个项目,定位到 #include <stdio.h>这一行,点击鼠标右键,在弹出的菜单中选择【打开文档 stdio.h】命令,在打开的头文件中使用查找命令(Ctrl F), 查找名称printf,找到printf函数的实现。另一种更快捷的方式是在函数名printf上点击右键,选择【转到定义】命令。然后会看到如下类似的代码。
是的,我们找到了printf的实现代码,可能看不懂。但这间接表明我们使用的C函数是有实现的,只不过不是在当前项目的.c文件中实现的。而是在.h文件中提供函数的实现。在头文件中定义函数是有一些限定规则,更多时候是在.c文件中实现函数的。
库函数我们编写了求平均值函数来方便处理成绩数据,同理,其他开发者也可以编写函数供我们使用,库函数便是如此。它是由第三方编写的供我们使用的可复用代码。C语言为我们提供了处理字符串,时间、文本等必要的函数。使用这些函数,通常叫做调用库函数(库的概念我们以后会讲解),比如我们使用mkdir函数来创建文件夹。
由寻找printf函数的实现过程可以看出,库函数对应的函数声明通常在某个头文件。如使用mkdir时,需要包含direct.h。当你运行上面的程序时,它会在D盘下创建一个名为abc的文件夹。
初遇有了寻找printf函数实现的经验,你可能也有兴趣寻找mkdir函数的实现。在direct.h中我们会看到如下代码:
好像有些不按套路出牌!对于printf函数,我们虽然读不懂,但至少能看到一对大括号包含的函数体实现。而mkdir,我们仅能看到一个类似函数声明的复杂表示(事实上就是函数声明)。说好的没有实现程序就不能成功链接呢?秘密在于mkdir的实现存在于某个库中,这便是C语言运行时库。上面的程序之所以能够编译成功,是因Visual Studio暗地里为我们做了一些工作,得以保证能能在C语言运行时库中找到mkdir的实现。
可以看出,有些函数在.h(更多的是.c)文件中以源代码的形式提供了函数的实现,库则是以别一种方式(通常是二进制形式)提供了函数实现的文件。当生成程序时,编译器从包含的文件中寻找被调用的函数声明,以保证函数使用的正确性,如参数个数,参数类型等。链接时再从库或实现文件中找到对应的定义,这样程序便会生成成功。
现在不必太过纠结库的概念,只需要保证会像mkdir那样使用库函数即可。等我们自己编写了库之后,一切将会真相大白。
,