我想知道,如果我有这样的源文件
#include <stdio.h>
int main()
{
printf("Hello world");
}
据我所知,头文件只包含函数的原型。如果是这样,我的源文件如何获得函数 printf ?如果我不包含已声明的源文件?谢谢你
问题尚不清楚,但在创建程序之前会发生两件事,
编译(需要原型/声明)
链接(需要定义)。
了解原型需要标头信息。即使这样编译也很好:
int printf ( const char * format, ... );
int main()
{
printf("Hello world");
}
在链接时不会有任何问题,因为该printf
函数是在 C 标准库中找到的,因此在链接时它将查看标准目录(保存库的编译器的 bin/lib 文件夹)并链接该函数。
源只需要知道原型。在这种情况下,程序员会遇到的问题:
int my_printf ( const char * format, ... );
int main()
{
my_printf("Hello world");
}
上面会编译,但是在链接my_printf
你的代码时没有定义,所以它会在链接时出错。
头文件具有函数的定义声明(stdio.h
具有定义声明printf
)。实际函数存在于库中,并在编译代码时被链接。
在使用库时,您在代码中包含库的标头,并指示链接器使用该库的代码文件(通常是目标文件)进行链接。对于标准库,IDE 通常会指示链接器默认链接到它们。
假设您使用 gcc 作为编译器,标准库默认链接,这就是函数定义所在的位置。如果您想确切查看正在链接的库,您可以传递 gcc 的 -v 选项,这将导致它转储有关它将使用的默认选项的信息,包括库路径和默认库以及将链接的目标文件.
如果您提供 -Wl,--verbose 选项,gcc 会将 --verbose 传递给链接器,链接器将准确转储它正在寻找库的位置,包括失败和成功的搜索
gcc -v foo.c -Wl,--verbose
头文件stdio.h
将声明printf
为extern
函数,即,它在别处定义。只要您使用的函数有声明,编译器就会很高兴。链接器是解决这些依赖关系的一个。
当您开始提出这样的好问题时,一件非常有用的事情是使用一些链接器命令。
假设你在 *nix 上,一旦你有你的可执行文件(让我们称之为foo
),做:
ldd foo
您应该会看到创建时链接的库列表foo
。
libc.so
应该是其中之一。它包含printf
除其他外的定义!
您可以参考此链接了解更多信息