如果我只包含头文件,编译器如何找出我的代码中将使用哪个动态链接库,哪里没有描述它?
#include <stdio.h> void main() { printf("Hello world\n"); }
有我只包括
stdio.h
我的代码被用来
printf function
如何知道,在头文件原型,宏和常量被描述,但没有关于在哪个文件“printf”是实现。那么它是如何工作的?
ggazkfy81#
当你编译一个可运行的可执行文件时,你不仅要指定源代码,还要指定一个库列表,从这些库中可以查找到未定义的引用。对于C标准库,这是隐式的(除非你告诉GCC -nostdinc),所以你可能没有意识到这一点。库只被链接器使用,而不是编译器。链接器定位库中所有未定义的引用。如果库是静态的,链接器只会将实际的机器码添加到最终的可执行文件中。另一方面,如果库是共享的,链接器只记录名称(和版本?)。然后是 loader 的工作以便在加载时找到合适的库并即时解决缺少的依赖项。在Linux上,您可以使用ldd来列出动态链接的可执行文件的加载时间依赖项,例如,尝试ldd /bin/ls。(在MacOS上,您可以使用otool -L来实现相同的目的。)
-nostdinc
ldd
ldd /bin/ls
otool -L
x8diyxa72#
正如其他人所回答的,标准c库是隐式链接的。如果你正在使用gcc,你可以使用-Wl,--trace选项来查看链接器在做什么。我测试了您的示例代码:
-Wl,--trace
gcc -Wl,--trace main.c
提供:
/usr/bin/ld: mode elf_x86_64 /usr/lib/gcc/x86_64-linux-gnu/4.6/../../../x86_64-linux-gnu/crt1.o /usr/lib/gcc/x86_64-linux-gnu/4.6/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/4.6/crtbegin.o /tmp/ccCjfUFN.o -lgcc_s (/usr/lib/gcc/x86_64-linux-gnu/4.6/libgcc_s.so) /lib/x86_64-linux-gnu/libc.so.6 (/usr/lib/x86_64-linux-gnu/libc_nonshared.a)elf-init.oS /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 -lgcc_s (/usr/lib/gcc/x86_64-linux-gnu/4.6/libgcc_s.so) /usr/lib/gcc/x86_64-linux-gnu/4.6/crtend.o /usr/lib/gcc/x86_64-linux-gnu/4.6/../../../x86_64-linux-gnu/crtn.o
这表示连接器正在使用libc.so(以及ld-linux.so)。
ryhaxcpt3#
GCC默认链接glibc库。在构建可执行文件时,不需要提及-l library。因此,您会发现printf函数和glibc中的其他函数不需要任何独占链接。
glibc
-l library
printf
1rhkuytd4#
从技术上讲,编译器并不知道要使用哪些库。链接器(通常是ld)会这样做。头文件只告诉编译器库函数使用的接口,并让链接器来确定它们在哪里。一个源文件在变成可执行文件之前会有一个很长的路径。c-[预处理]〉i -[编译]〉s -[汇编]〉o -[链接]〉a.out当您调用cc source.c时,所有这些步骤都是一次透明地完成的,并且标准库(通常为libc.so)和可执行加载程序(通常为crt0.o)链接在一起。任何附加库必须作为附加链接器标志(即-lpthread)传递。
ld
cc source.c
libc.so
crt0.o
-lpthread
qgzx9mmu5#
我会说这取决于IDE或者编译器和系统。头文件只包含接口信息,比如函数参数的名称,它期望任何属性,其他的,这就是编译器如何首先将你的代码转换成一个中间目标文件。然后是链接,在代码中printf通过静态库或动态库被添加到可执行文件中。函数和其他工具(如STL)是C/C++的一部分,因此它们要么由编译器提供,要么由系统提供。例如,在Solaris上,除非使用gcc,否则没有C库的调试版本。但在Visual Studio上,您有调试版本msvcrt.dll,并且还可以静态链接C库。简而言之,答案是C库中printf和其他函数的代码是由编译器在链接时添加的。
5条答案
按热度按时间ggazkfy81#
当你编译一个可运行的可执行文件时,你不仅要指定源代码,还要指定一个库列表,从这些库中可以查找到未定义的引用。对于C标准库,这是隐式的(除非你告诉GCC
-nostdinc
),所以你可能没有意识到这一点。库只被链接器使用,而不是编译器。链接器定位库中所有未定义的引用。如果库是静态的,链接器只会将实际的机器码添加到最终的可执行文件中。另一方面,如果库是共享的,链接器只记录名称(和版本?)。然后是 loader 的工作以便在加载时找到合适的库并即时解决缺少的依赖项。
在Linux上,您可以使用
ldd
来列出动态链接的可执行文件的加载时间依赖项,例如,尝试ldd /bin/ls
。(在MacOS上,您可以使用otool -L
来实现相同的目的。)x8diyxa72#
正如其他人所回答的,标准c库是隐式链接的。如果你正在使用gcc,你可以使用
-Wl,--trace
选项来查看链接器在做什么。我测试了您的示例代码:
提供:
这表示连接器正在使用libc.so(以及ld-linux.so)。
ryhaxcpt3#
GCC默认链接
glibc
库。在构建可执行文件时,不需要提及-l library
。因此,您会发现printf
函数和glibc
中的其他函数不需要任何独占链接。1rhkuytd4#
从技术上讲,编译器并不知道要使用哪些库。链接器(通常是
ld
)会这样做。头文件只告诉编译器库函数使用的接口,并让链接器来确定它们在哪里。一个源文件在变成可执行文件之前会有一个很长的路径。
c-[预处理]〉i -[编译]〉s -[汇编]〉o -[链接]〉a.out
当您调用
cc source.c
时,所有这些步骤都是一次透明地完成的,并且标准库(通常为libc.so
)和可执行加载程序(通常为crt0.o
)链接在一起。任何附加库必须作为附加链接器标志(即
-lpthread
)传递。qgzx9mmu5#
我会说这取决于IDE或者编译器和系统。头文件只包含接口信息,比如函数参数的名称,它期望任何属性,其他的,这就是编译器如何首先将你的代码转换成一个中间目标文件。然后是链接,在代码中printf通过静态库或动态库被添加到可执行文件中。
函数和其他工具(如STL)是C/C++的一部分,因此它们要么由编译器提供,要么由系统提供。例如,在Solaris上,除非使用gcc,否则没有C库的调试版本。但在Visual Studio上,您有调试版本msvcrt.dll,并且还可以静态链接C库。
简而言之,答案是C库中printf和其他函数的代码是由编译器在链接时添加的。