我的第一个例子不起作用,但我的第二个例子确实有效。所以这让我想问一下链接时编译的正确方法是什么?是否有关于正确链接的更多信息?
不工作:gcc $(curl-config --libs --cflags) prog.c -o prog
工作:gcc prog.c $(curl-config --libs --cflags) -o prog
答案 0 :(得分:5)
C编译器 - 它是GCC还是任何其他编译器并不重要 - 是一个复杂的生物。它实际上调用单独的进程来完成工作的不同部分。有解析器,可能(现在通常会)包括预处理器部分,然后是代码生成,通常是汇编程序,然后是汇编程序生成对象,最后是程序链接(通常由名为ld
的程序,对于装载机)。详细的组织可以是不同的;这里的关键点是加载器对早期阶段创建的目标文件以及它被告知要使用的库进行操作。
当您编写调用以链接可执行文件时,您可能也可能不会将代码编译到目标文件。在您的示例中,您将prog.c
编译为prog.o
;通常,link命令只列出目标文件而不是任何源文件。
使用C编译器添加的选项(以及可能的目标文件)调用链接器,然后是目标文件和库,并按照在命令行上列出的顺序链接相关选项,以及它自己添加的任何库
有两种类型的链接 - 静态和动态。使用静态链接,可执行文件包含它将在运行时使用的所有对象代码,除了在运行时动态加载的任何内容。链接器在遇到目标文件和库时处理它们。通常,第一个目标文件被称为类似crt0.o
的东西,由编译器提供;它包含对main()
的引用。链接器通过记录它定义的符号及其引用的未定义的符号来处理目标文件。
对于静态链接,它会在库遇到它们时对其进行扫描;如果库提供了当前未定义的符号,则它会从库中提取相关代码,并根据需要重新扫描以查找已引用但尚未定义的其他符号。如果唯一未定义的符号是main()
且库不包含main()
(这是正常的),则跳过库有效。
对于动态链接(使用共享对象或动态链接库,也就是DLL),过去就是这种情况(在某些机器上),如果你在链接行上提到了一个库,那么所有符号都会自动被视为已定义,他们是否被使用过。最近,如果编译器在扫描链接过程中的点上不包含任何相关符号,则忽略库。
您的工作示例显示了正确的链接顺序 - 目标文件之后的库。当然,在您的示例命令行中,列出了源文件,但编译器将命令转换为引用它刚刚从源创建的目标文件,并在命令行中列出序列中的库和其他链接器标志。如果您有一个旧式链接器记录了共享对象的使用,无论它是否满足任何未定义的引用,“非工作”行也会链接OK。然而,它永远不可靠;如果任何库是一个静态库,你通常会遇到问题。
所以,规则很简单:
它始终有效 - 新旧连接器,静态和动态库。在某些平台上,迟早会以任何其他方式执行此操作。
答案 1 :(得分:0)
这是gcc语法。我希望你理解为什么第一个不工作,第二个是:
GCC语法
$ gcc [options] [source files] [object files] [-o output file]