我们举个例子:
#include
int main( int argc, char ** argv )
{
printf("hello\n");
exit(56);
}虽然我们在编译时收到警告:
~$ gcc test.c -o test
test.c: In function ‘main’:
test.c:6:5: warning: implicit declaration of function ‘exit’ [-Wimplicit-function-declaration]
exit(56);
^
test.c:6:5: warning: incompatible implicit declaration of built-in function ‘exit’
test.c:6:5: note: include ‘’ or provide a declaration of ‘exit’无论如何,我认为你已经尝试运行它并确保它有效:
~$ ./test
hello
~$ echo $?
56@Mat说得对:
You're mixing up including headers and linking to libraries. Those are
two completely different things
C编译器和链接器是完全独立的工具。我们来看看吧。实际上,这个程序依赖于标准C库(如果没有将-nostdlib传递给编译器,则为所有程序)和一些系统库(如loader和vdso)。您可以看到它:
~$ ldd test
linux-vdso.so.1 (0x00007fff1b128000)
libc.so.6 => /lib64/libc.so.6 (0x00007f804389f000)
/lib64/ld-linux-x86-64.so.2 (0x0000557744537000)这三个库是任何程序的最小集合。 exit函数在我们的案例中在标准库或libc.so.6中定义。现在让我们以详细模式查看编译过程。您可以通过将-v或--verbose选项传递给编译器来实现此目的:
gcc test.c -o test --verbose如果你执行这个,你会发现这样的行:
#include <...> search starts here:
/usr/lib/gcc/x86_64-redhat-linux/5.3.1/include
/usr/local/include
/usr/include因此,编译知道在哪里搜索stdlib的头文件,并开始搜索它以查找非本地函数的声明。请注意,它仅搜索源代码文件中包含的头文件。它可以在stdio.h中找到printf声明,但无法找到exit的声明。
完成此步骤后,编译开始将程序与库链接:
/usr/libexec/gcc/x86_64-redhat-linux/5.3.1/collect2 ... -lc ...其中collect2是gcc util,它试图将您的程序链接到标准C库lc。请注意,该过程包括两个步骤:编译和链接。这就是你的程序有效的原因。
此外,gcc支持-M选项,该选项将告诉您主文件的依赖关系。因此,如果您将执行它,您将看到一组头文件,包括stdio.h,但不是stdlib.h:
$ gcc -M test.c
test.o: test.c /usr/include/stdc-predef.h /usr/include/stdio.h \
/usr/include/features.h /usr/include/sys/cdefs.h \
/usr/include/bits/wordsize.h /usr/include/gnu/stubs.h \
/usr/include/gnu/stubs-64.h \
/usr/lib/gcc/x86_64-redhat-linux/5.3.1/include/stddef.h \
/usr/include/bits/types.h /usr/include/bits/typesizes.h \
/usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \
/usr/lib/gcc/x86_64-redhat-linux/5.3.1/include/stdarg.h \
/usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h或者甚至更好,尝试将-E选项传递给gcc:
$ gcc -E test.c你会在第一阶段 - 预处理阶段之后看到结果。我认为这是了解为什么会收到此警告的最简单方法。