Link

主要查看了 CSAPP

从 C 程序到可执行文件, 总共经过了这样几个部分:

1. Preprocess

在C语言中预处理器 (C PreProcessor)是一种特殊的程序用于在编译过程之前对源代码进行处理预处理器会根据以 # 开头的预处理指令对源代码进行不同形式的转换例如宏替换文件包含等操作

当我们使用C语言编写程序时源代码通常包含三个部分头文件Header File函数定义和主函数预处理器的作用就是将这些源代码文件合并起来并生成一份纯C文件也称为翻译单元Translation Unit纯C文件中只包含了最终要编译的源代码而没有任何预处理指令和注释

生成纯C文件的过程通常可以通过命令行工具 gcc -E 来执行其中 -E 参数表示只进行预处理不进行编译和链接例如假设我们有一个名为 test.c 的源文件可以使用以下命令将其转换为纯C文件

复制代码gcc -E test.c -o test.i

其中-o 参数指定了输出文件的名称为 test.i(也可以是.c格式的)该文件即为生成的纯C文件

生成纯C文件的主要目的是方便调试和分析源代码由于纯C文件已经去除了预处理指令和注释因此可以更加方便地查看和分析源代码中真正要编译的部分此外生成纯C文件还可以帮助我们理解预处理器的工作原理并调试和优化预处理器宏和指令

hello.i文件居然有550行

上图是对 hello.c 程序进行操作, 生成的纯C中间文件居然多达550行

.i 格式 和 .c 格式有什么区别

.i 格式和 .c 格式都是C语言的源文件格式它们之间的区别在于

  1. 后缀不同.i 是纯C文件的后缀.c 是包含了所有源代码和预处理指令的源文件的后缀
  2. 是否经过预处理.i 文件是经过预处理器预处理后的纯C文件.c 文件则包含了所有的源代码和预处理指令在生成 *.i 文件时预处理器会对源代码中的预处理指令进行展开并替换为相应的代码同时还会去除注释和空行等无用信息最终得到一个只包含有效C代码的文件
  3. 用途不同.c 文件是编译器直接使用的源文件其中包含了所有需要编译的代码和预处理指令.i 文件通常用于调试和分析源代码它已经去除了预处理指令和无用信息方便用户查看和分析纯C代码

总之.i.c 文件都是C语言的源文件格式.i 文件是经过预处理器处理后的纯C文件只包含有效的C代码.c 文件则包含了所有的源代码和预处理指令.i 文件一般用于调试和分析源代码.c 文件是编译器直接使用的源文件

2. Compile

3.

链接

在计算机科学中链接Linking是将多个目标文件Object File或库文件Library组合成一个可执行文件的过程在程序开发过程中通常会将程序代码分为多个源文件进行编写和管理每个源文件经过编译后都会生成目标文件然后这些目标文件需要被链接起来才能生成最终的可执行文件

在链接的过程中链接器会将不同的目标文件中定义的函数变量等符号连接起来以便程序正确地执行具体来说链接器会完成以下几个主要任务

  1. 符号解析Symbol Resolution目标文件中使用到的未定义符号其他目标文件中定义的符号进行匹配并将它们连接起来
  2. 重定位Relocation由于目标文件中的地址空间与最终可执行文件中的地址空间可能会不同因此链接器需要对目标文件中的一些地址进行修正以便使它们在最终可执行文件中能够正确地访问目标对象
  3. 优化Optimization链接器还可以对目标文件中的代码进行优化以提高最终程序的性能和效率

在不同的操作系统和编程语言中链接的方式和实现都可能会有所不同在C语言中静态链接和动态链接是两种常见的链接方式它们使用的链接器也不同

链接分为静态链接和动态链接两种类型

静态链接

静态链接是指将所有目标文件和库文件直接链接到最终生成的可执行文件中的过程在静态链接期间编译器将目标文件中使用到的库文件中的函数和变量 直接复制 到最终生成的可执行文件中这使得最终的可执行文件中包含了所有需要的代码和数据可以独立地运行于系统环境中

具体来说做了两件事:

  • 空间和地址分配
  • 符号解析与重定位

优点

  • 可以在不安装任何其他软件的情况下将可执行文件直接复制到其他计算机上运行
  • 执行速度相对较快因为所有代码和数据都已经被复制到同一个文件中

缺点

  • 可执行文件比较大包含了所有需要的库代码和数据因此占用的存储空间较大
  • 如果多个程序使用同一份库文件则会出现代码重复的情况浪费存储空间
  • 如果库文件更新了需要重新链接并重新编译整个程序

动态链接

动态链接是指在程序运行时才从共享库文件 (Shared Library) 中加载所需的函数和数据的链接方式在动态链接期间编译器只将程序中使用到的库函数的名称记录在可执行文件中并不将函数的实现代码复制到可执行文件中当程序运行时操作系统会根据程序需要从共享库文件中动态加载所需的函数和数据

具体来说有四个步骤:

  • 动态链接器自举
  • 装载共享对象
  • 重定位与初始化
  • 转交控制权

优点

  • 可执行文件相对较小因为它只包含了函数名等链接信息而没有库代码和数据
  • 不同的程序可以共享同一份库文件节省存储空间
  • 如果库文件更新了不需要重新编译整个程序只需要替换共享库文件即可

缺点

  • 程序在运行时需要加载共享库文件会增加启动时间和内存使用量
  • 执行速度相对静态链接较慢因为需要从共享库中加载函数和数据

总之静态链接适合独立部署的应用程序而动态链接适合需要共享库文件的应用程序