0

我的第一个示例不起作用,但我的第二个示例确实有效。所以这让我问链接时编译的正确方法是什么?是否有更多关于正确链接的信息?

非工作:gcc $(curl-config --libs --cflags) prog.c -o prog

在职的:gcc prog.c $(curl-config --libs --cflags) -o prog

4

2 回答 2

5

C 编译器——不管它是 GCC 还是任何其他编译器——是一个复杂的生物。它实际上调用单独的进程来完成工作的不同部分。有解析器,它可能(这些天,通常会)包括预处理器部分,然后是代码生成,通常是汇编器,然后是汇编器生成对象,最后是程序链接(通常由一个名为 的程序ld,用于加载程序)。详细的组织可以不同;这里的关键点是加载器对早期阶段创建的目标文件以及它被告知要使用的库进行操作。

当您编写链接可执行文件的调用时,您可能也可能不将代码编译为目标文件。在您的示例中,您确实编译prog.cprog.o; 通常,链接命令只列出目标文件而不列出任何源文件。

使用 C 编译器添加的选项(可能还有目标文件)调用链接器,然后是目标文件和库,并按照您在命令行中列出它们的顺序链接相关选项,以及它自己添加的任何库。

有两种类型的链接——静态的和动态的。使用静态链接,可执行文件包含它将在运行时使用的所有目标代码,除了在运行时动态加载的任何内容。链接器在遇到目标文件和库时对其进行处理。通常,第一个目标文件被称为类似的东西crt0.o,由编译器提供;它包含对main(). 链接器通过记录它定义的符号和它引用的未定义符号来处理目标文件。

对于静态链接,它会在遇到库时对其进行扫描;如果库提供了一个当前未定义的符号,它会从库中提取相关代码,并根据需要重新扫描以查找其他已引用但尚未定义的符号。如果唯一未定义的符号是main()并且库不包含 a main()(这是正常的),则实际上跳过了库。

对于动态链接(使用共享对象或动态链接库,也称为 DLL),过去(在某些机器上)如果您在链接行上提到了一个库,所有符号都会被自动视为已定义,无论它们是使用与否。最近,如果一个库在被扫描的链接过程中不包含任何相关符号,编译器就会忽略它。

您的工作示例显示了正确的链接顺序 - 目标文件之后的库。当然,在您的示例命令行中,您列出了一个源文件,但编译器将命令转换为引用它刚刚从源创建的目标文件,并在命令行中按顺序列出库和其他链接器标志。如果您有一个旧式链接器,它记录了共享对象的使用,无论它是否满足任何未定义的引用,“非工作”行也可以链接。但是,它从来都不可靠。如果任何库是静态库,您通常会遇到问题。

所以,规则很简单:

  • 链接程序时在库之前列出目标文件。

它总是有效的——新旧链接器、静态和动态库。在某些平台上,任何其他方式迟早都会失败。

于 2013-06-05T01:48:35.367 回答
0

这是 gcc 语法。我希望你明白为什么第一个不起作用,而第二个是:

GCC 语法

$ gcc [选项] [源文件] [目标文件] [-o 输出文件]

于 2013-06-05T00:54:56.220 回答