每当我们创建一个包含自定义函数定义的文件,例如,以及包含函数声明的utils.c
相应头文件时,我必须使用类似 的命令将该文件与我正在使用的驱动程序代码一起编译。utils.h
utils.c
gcc driver.c utils.c -o my_exe
那么什么指令可以编译我们包含其头文件的标准 C 文件stdio.h
?
每当我们创建一个包含自定义函数定义的文件,例如,以及包含函数声明的utils.c
相应头文件时,我必须使用类似 的命令将该文件与我正在使用的驱动程序代码一起编译。utils.h
utils.c
gcc driver.c utils.c -o my_exe
那么什么指令可以编译我们包含其头文件的标准 C 文件stdio.h
?
编译分为三个步骤:预处理、编译和链接。
gcc driver.c utils.c -o my_exe
是以下的简写:即编译驱动程序、编译实用程序,然后将所有内容链接在一起。
这里仍然不太清楚的是,includes 被复制到 c 文件中进行编译。以下步骤进一步分解,以显示处理头文件中代码的预处理过程:
gcc driver.o utils.o -o my_exe
也可以更改包含库,例如-lm
包含标准数学库(libm.a 或 libm.so)。库只是.o
容器文件中预编译的 C 源代码(目标代码或)。标准库(如
stdlib.h
和stdio.h
)是libc.so
(或libc.a
)。您无需显式链接它们,但如果您愿意,也可以这样做。然后
gcc driver.o utils.o -o my_exe
变成gcc driver.o utils.o -o my_exe -lc
您可以按照相同的方式预编译代码以供稍后链接:
使用它进行编译时,
gcc driver.c utils.c -o my_exe
将跳过创建相应的 .o 文件。由于最终输出需要 driver.c 和 utils.c 的更改,因此每次这两个文件发生任何更改时,您都需要运行此命令。如上一条回复所述,标准 c 文件被编译为静态或动态库,这些库将由相应的软件包所有者创建,并作为任何操作系统软件包的一部分供用户使用,例如 libc-dev。为了避免在特定文件中没有修改时进行编译,您可以使用 Makefile 创建一个中间的“.o”文件,例如
在大多数 C 实现中,标准 C 库的源代码已经为您准备好并打包到“库”文件中。
标准库的源代码被编译或汇编成目标模块,这些目标模块被插入到静态或动态库文件中。库文件很大程度上只是目标模块的集合。
GCC 和 Clang 在链接时默认包含标准库。(这会带来一些麻烦,例如某些实现需要额外的
-lm
开关才能包含标准库的数学例程。)gcc
orclang
命令(以及其他一些变体,例如g++
)实际上是一个“前端”,它会根据您传递的参数的需要,调用编译器和/或汇编器和/或链接器。如果您使用开关-v
,它们将显示它们执行的命令,并且链接命令将包含一个或多个库文件。当您包含
#include <stdio.h>
(添加输入/输出函数,如printf
)用于内存管理#include <stdlib>
、字符串操作#include <string.h>
等时,会发生这种情况...您告诉编译器复制声明printf
,scanf
等...这些函数位于头文件中,如stdio.h
,stdlib
等...这些文件的代码已经编译,它们是 GNU C 库的一部分。您可以尝试详细了解这些文件的位置
gcc -v your_program.c -o your_program
输出将如下所示
这里你可以看到三个阶段:
通过调用和将文件编译
.c
为目标文件。.o
cc1
as
将这些目标文件与启动文件(
Scrt1.o
、、)和预建的 C 运行时库(GCC 的支持库和 C 标准库)crti.o
链接在一起。crtn.o
结果就是您的最终可执行文件。
在详细转储中,关键行隐藏在
collect2
/ld
调用中:… -plugin-opt=-pass-through=-lc … -lc …
这
-lc
是链接器标志,它告诉它:您无需自行编译
stdio.c
(或编译任何.c
glibc 源代码)。C 库以预编译的存档(libc.a
)和共享对象(libc.so
)的形式提供,GCC 驱动程序会-lc
在链接时自动传递,以便您的所有<stdio.h>
声明都能解析为 libc 中的真实代码。值得一读的是https://www.gnu.org/software/libc/manual/html_node/Header-Files.html以及编译/链接过程如何工作?
标准 C 文件已经编译完毕,并且是 stdc++ 库及其链接库的一部分。在我的例子中,它位于
一个示例测试,用于检查 是否
.so
包含函数。我只是检查了 是否printf
存在于 中libstdc++.so.6
。每个 gcc 版本都有对应的版本
libstdc++.so
,因此无法在低版本的 gcc 中运行用高版本 gcc 构建的可执行文件。因为低版本的 gcc 缺少所需的运行时符号。