使用GCC编译C项目时采用了哪些默认库

时间:2016-01-09 11:51:55

标签: c gcc

我的申请很简单:

#include <stdio.h>
int main( int argc, char ** argv )
{
    printf( "hello");
    exit( 0 );
}

当我用命令

编译它时
gcc -c count_words.c

我有警告:

warning: incompatible implicit declaration of built-in function ‘exit’ [enabled by default]
     exit( 0 );

我试图找到定义exit()函数的位置。并发现它在stdlib.h中定义。但它不包含在我的项目中,并且在编译命令中没有定义其他库。

如果我错了,请纠正我,但看起来gcc默认使用一些库。什么是这些库,是否可以告诉gcc不包括它们?

为什么编译器对于exit(0)感到不满意,假设它以某种方式包含stdlib.h作为默认值?

4 个答案:

答案 0 :(得分:5)

我们举个例子:

#include <stdio.h>

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 ‘<stdlib.h>’ or provide a declaration of ‘exit’

无论如何,我认为你试图运行它并确保它有效:

~$ ./test
hello
~$ echo $?
56

@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)

这三个库是任何程序的最小集合。 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的头文件,并开始搜索它以查找非本地函数的声明。请注意,它仅搜索源代码文件中包含的头文件。它可以在printf中找到stdio.h声明,但找不到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

您将在第一阶段 - 预处理阶段之后看到结果。我认为这是了解你为何会收到此警告的最简单方法。

答案 1 :(得分:1)

  

假设它以某种方式包含stdlib.h作为默认值?

根本没有。这不符合标准。

因此,当你使用exit时,它从未被声明过,所以你在使用时会有隐式声明。但是,在其他地方实施exit的事实只会在最终的联系中成为问题。

现在,海湾合作委员会非常清楚你即将遇到麻烦,所以它会警告你。

实际上只包括stdlib.h,你应该没问题。

答案 2 :(得分:1)

有问题的“图书馆”是gcc源代码树中的gcc / builtins.def。

这不仅仅是一个头文件,它不提供您使用的声明。但它有适当的原型来检查你的。

调用未声明的exit()会导致隐式声明,如下所示:

void exit(int) __attribute__((nothrow,noreturn));

搜索“c隐式声明”以获取血腥细节。在这种情况下,GCC从builtins.def中有自己的exit()原型:

{{1}}

这些声明不匹配,这就是你被警告的内容。

答案 3 :(得分:-5)

您的代码使用警告而不是错误。如果你添加

#include <stdlib.h>

然后再没有警告;)