包含和链接真的有什么用?有什么区别?为什么我需要同时指定它们?
当我写#include math.h
然后编写-lm
来编译它时,#include math.h
和-lm
分别做了什么?
根据我的理解,在链接库时,您需要其.h文件及其.o文件。这是否表明#include math.h
表示在{。1}}接收.o文件时接受.h文件?
答案 0 :(得分:3)
你需要一个头(接口描述)和库(实现)的原因是C将两者分开,而不是像C#或Java那样的语言。可以编译一个C函数(例如通过调用gcc -c <sourcefile>
),即使被调用的库不存在,它也会调用库代码;包含接口描述的标题就足够了。 (这对于C#或Java来说是不可能的;必须存在程序集和类文件/ jar文件。)在链接阶段,虽然库必须在那里,即使它是动态的,也可以是afaik。
相比之下,使用C#,Java或脚本语言,实现包含定义接口所需的所有信息。编译器(与链接器没有明确分离)查找jar文件或包含被调用实现的C#程序集,并从那里获取有关函数签名和类型的信息。
理论上,该信息可能也存在于用C编写的库中 - 它基本上就是调试信息。但是经典的C编译器(与链接器相对)无视库或目标文件,无法解析它们。 (应该记住,您通常用于编译C程序的“编译器”可执行文件,例如gcc,是一个“编译器驱动程序”,它解释命令行参数并调用实际执行操作的程序,例如预处理程序,实际编译器和实际的链接器,以创建所需的输出。)
所以理论上,如果你在一个已知的位置有一个正确注释的库,你可能会编写一个编译器来编译一个C函数,而不需要函数声明和类型定义;编译器必须生成适当的声明。编译器必须知道要解析哪个库(这对应于在VS中设置C#项目“引用”或在Java中具有类路径和名称/类对应)。
最简单的方法是使用众所周知的调试格式,如stabs或dwarf,并使用一个小帮助程序从中提取接口定义,该程序使用API作为调试格式,提取信息并生成一个C头。是每个源文件的前缀。这将是编译器驱动程序的工作,实际的编译器仍然会忘记它。
答案 1 :(得分:2)
因为头文件只包含声明,而.o文件(或类似的东西,如.obj,.dll或.lib)包含方法的定义。 如果打开.h文件,则不会看到方法代码,因为它位于库中。 一个原因是商业性的,因为您需要公开您的代码并在您的公司中拥有源代码。库已编译,因此您可以发布它。 头文件只说编译器,它可以在库中找到哪些类和方法。
答案 2 :(得分:1)
头文件是一种目录加上一种编译器的字典。它告诉编译器库提供了什么,并给出特殊值可读名称。
库文件本身包含内容。
答案 3 :(得分:0)
你所问的完全是两件不同的事情。 别担心,我会向你解释。 您使用#符号指示预处理器包含math.h头文件,这些文件内部包含fabs(),ceil()等函数原型。 并且您使用-lm来指示链接器,在exe文件中包含fabs(),ceil()等函数的预编译函数定义。 现在,您可能会问为什么我们必须显式链接数学函数的库文件,这与其他函数不同,答案是,这是由于一些未定义的历史原因。