好的,直到今天早上,我对这些术语完全感到困惑。我想我已经有所不同了。

首先,令人困惑的是,由于预处理器已经将头文件包含在包含函数的代码中,链接器将哪些库函数链接到由汇编器/编译器生成的目标文件?造成混乱的部分原因主要是由于我对头文件和库之间的差异一无所知。

经过一番谷歌搜索和堆栈溢出(这是术语?:p)之后,我收集到头文件主要包含函数声明,而实际实现是在另一个称为库的二进制文件中(我仍然不是100%确定这一点)。

因此,假设在以下程序中:

#include<stdio.h>
int main()
{
      printf("whatever");
      return 0;
}


预处理器在代码中包含头文件的内容。编译器/编译器+汇编器完成其工作,然后链接程序最终将此目标文件与另一个实际存储了printf()工作方式的目标文件合并。

我的理解正确吗?我可能要离开...所以,您能帮我吗?

编辑:我一直想知道有关C ++ STL。它总是让我困惑的是它到底是什么,所有这些标头的集合还是什么?现在,在阅读响应之后,我可以说STL是一个目标文件/类似于目标文件的东西吗?

而且,我还以为我可以在哪里读取pow()sqrt()等功能的函数定义。我会打开头文件,但找不到任何内容。那么,库中的函数定义是否为二进制不可读形式?

最佳答案

AC源文件经历两个主要阶段,(1)预处理程序阶段,其中预处理程序实用程序处理C源代码,该程序查找预处理程序指令并执行这些动作,(2)编译阶段,然后处理已处理的C源代码实际编译以生成目标代码文件。

预处理器是执行文本操作的实用程序。它以一个包含文本(通常为C源代码)的文件作为输入,该文本可能包含预处理程序指令,并通过将找到的所有指令应用于文本输入以生成文本输出,从而输出文件的修改版本。

该文件不必是C源代码,因为预处理器正在执行文本操作。我已经看到C Preprocssor用于扩展make实用程序,方法是允许将preprossor指令包含在make文件中。带有C Preprocessor指令的make文件是通过C Preprocessor实用程序运行的,结果输出随后馈入make以进行make目标的实际构建。

库和链接

库是包含各种功能的目标代码的文件。这是一种将多个源文件的输出打包到一个文件中时将其打包的一种方法。很多时候,库文件与头文件(包含文件)一起提供,通常具有.h文件扩展名。头文件包含函数声明,全局变量声明以及该库所需的预处理程序指令。因此,要使用该库,请包括使用#include指令提供的头文件,并与该库文件链接。

库文件的一个不错的功能是您提供的是源代码的编译版本,而不是源代码本身。另一方面,由于库文件包含已编译的源代码,因此用于生成库文件的编译器必须与用于编译自己的源代码文件的编译器兼容。

常用的库有两种。第一种和较旧的类型是静态库。第二个也是更新的是动态库(Windows中为动态链接库或DLL,而Linux中为共享库或SO)。两者之间的区别在于,库中的功能何时绑定到使用库文件的可执行文件。

链接器是一个实用程序,可使用各种目标文件和库文件来创建可执行文件。当在C源文件中使用外部或全局函数或变量时,一种标记用于告知链接器该点需要插入函数或变量的地址。

C编译器仅知道其编译的源代码中的内容,而不知道其他文件(如目标文件或库)中的内容。因此,链接器的工作是获取各种目标文件和库,并通过用实际连接替换标记来在零件之间建立最终连接。因此,链接器是一种实用程序,可将各种组件“链接”在一起,用指向为该全局函数或变量生成的实际目标代码的链接替换目标文件和库中的全局函数或变量的标记。

在链接器阶段,静态库与动态库或共享库之间的区别变得明显。使用静态库时,该库的实际目标代码包含在应用程序可执行文件中。使用动态库或共享库时,应用程序可执行文件中包含的目标代码是用于查找共享库并在应用程序运行时与其连接的代码。

在某些情况下,可以在多个不同的目标文件或库中使用相同的全局函数名称,因此链接器通常仅使用遇到的第一个,并发出关于发现其他警告的警告。

编译和链接摘要

因此,C程序的编译和链接的基本过程是:


预处理程序实用程序生成要编译的C源代码
编译器将C源代码编译为目标代码,从而生成一组目标文件
链接器将各种目标文件以及任何库链接到可执行文件中


上面是基本过程,但是当使用动态库时,它会变得更加复杂,特别是如果正在生成的部分应用程序具有要生成的动态库。

装载机

还有一个阶段,何时将应用程序实际加载到内存中并开始执行。操作系统提供了一个实用程序,即加载程序,它可以读取应用程序的可执行文件并将其加载到内存中,然后启动应用程序的运行。可执行文件的起始点或入口点是在可执行文件中指定的,因此在加载程序将可执行文件读入内存后,它将通过跳至入口点内存地址来启动可执行文件的运行。

链接器可能遇到的一个问题是,有时在处理需要实际内存地址的目标代码文件时,链接器可能会遇到标记。但是,链接器不知道实际的内存地址,因为该地址将根据应用程序在内存中的加载位置而变化。因此,链接器将其标记为加载器实用程序在加载器将可执行文件加载到内存中并准备开始运行时修复的问题。

对于具有硬件支持的虚拟地址到物理地址映射或转换的现代CPU,实际内存地址的这个问题很少出现。每个应用程序都加载到相同的虚拟地址,并且硬件地址转换处理实际的物理地址。但是,缺少用于地址转换的内存管理单元(MMU)硬件支持的老式CPU或低成本CPU(例如微控制器)仍然需要解决此问题。

入口点和C运行时

最后一个主题是C运行时以及main()和可执行文件入口点。

C运行时是编译器制造商提供的目标代码,其中包含用C编写的应用程序的入口点。main()函数是程序员编写应用程序时提供的入口点,但这不是装载机看到。启动应用程序后,C运行时将调用main()函数,并且C运行时代码为应用程序设置环境。

C运行时不是标准C库。 C Runtime的目的是管理应用程序的运行时环境。标准C库的目的是提供一组有用的实用程序函数,以便程序员不必创建自己的函数。

当加载程序加载应用程序并跳转到C运行时提供的入口点时,C运行时将执行所需的各种初始化操作,以为应用程序提供适当的运行时环境。完成此操作后,C运行时将调用main()函数,以便应用程序开发人员或程序员创建的代码开始运行。当main()返回或调用exit()函数时,C运行时将执行清理和关闭应用程序所需的任何操作。

关于c - -1)预处理器,链接器,2)Header文件,库之间有什么区别?我的理解正确吗?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/12177796/

10-15 00:28
查看更多