我有简单的应用:
#include <stdio.h>
int main( int argc, char ** argv )
{
printf( "hello");
exit( 0 );
}
Run Code Online (Sandbox Code Playgroud)
当我用命令编译它
gcc -c count_words.c
Run Code Online (Sandbox Code Playgroud)
我有警告:
warning: incompatible implicit declaration of built-in function ‘exit’ [enabled by default]
exit( 0 );
Run Code Online (Sandbox Code Playgroud)
我试图找到exit()定义函数的位置.并发现它是在中定义的stdlib.h.但它不包含在我的项目中,并且在编译命令中没有定义其他库.
如果我错了,请纠正我,但看起来像是gcc默认的一些库.什么是这些库,是否可以告诉gcc不包括它们?
为什么编译器在exit(0)假设它以某种方式包含stdlib.h为默认值时不满意?
我们举个例子:
#include <stdio.h>
int main( int argc, char ** argv )
{
printf("hello\n");
exit(56);
}
Run Code Online (Sandbox Code Playgroud)
虽然我们收到编译警告:
~$ 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 ‘<stdlib.h>’ or provide a declaration of ‘exit’
Run Code Online (Sandbox Code Playgroud)
无论如何,我认为你已经尝试运行它并确保它有效:
~$ ./test
hello
~$ echo $?
56
Run Code Online (Sandbox Code Playgroud)
当他说:@Mat说得对:
你混合包括标题和链接到库.这是两件完全不同的事情
该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)
Run Code Online (Sandbox Code Playgroud)
这三个库是任何程序的最小集合.该exit函数在标准库或libc.so.6我们的案例中定义.现在让我们以详细模式查看编译过程.您可以通过传递-v或--verbose编译器选项来实现此目的:
gcc test.c -o test --verbose
Run Code Online (Sandbox Code Playgroud)
如果您将执行此操作,您将找到以下行:
#include <...> search starts here:
/usr/lib/gcc/x86_64-redhat-linux/5.3.1/include
/usr/local/include
/usr/include
Run Code Online (Sandbox Code Playgroud)
因此,编译知道搜索头文件的位置,stdlib并开始搜索它以查找非本地函数的声明.请注意,它仅搜索源代码文件中包含的头文件.它可以在中查找printf声明stdio.h,但无法定位声明exit.
完成此步骤后,编译开始将程序与库链接:
/usr/libexec/gcc/x86_64-redhat-linux/5.3.1/collect2 ... -lc ...
Run Code Online (Sandbox Code Playgroud)
collect2gcc util试图链接你的程序lc是哪里的标准C库.请注意,该过程包括两个步骤:编译和链接.这就是你的程序运作的原因.
此外,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
Run Code Online (Sandbox Code Playgroud)
或者甚至更好,尝试将-E选项传递给gcc:
$ gcc -E test.c
Run Code Online (Sandbox Code Playgroud)
你会在第一阶段 - 预处理阶段之后看到结果.我认为这是了解为什么会收到此警告的最简单方法.
假设它以某种方式包含 stdlib.h 作为默认值?
事实并非如此。这不符合标准。
因此,当您使用 时exit,它从未被声明过,因此在使用时存在隐式声明。然而,事实上你exit在其他地方都没有实现,只会成为最终链接的问题。
现在,GCC 非常清楚您即将遇到麻烦,因此它会向您发出警告。
只需实际包含 stdlib.h 就可以了。