本文目录
当我们编写C语言程序时,需要经过四个主要的编译过程,它们是:预处理、编译、汇编和链接。下面逐个解释这四个过程的详细步骤。
1. 预处理(Preprocessing)
预处理是指在编译之前对源代码进行的处理。它主要包括以下几个步骤:
- 处理头文件引用:将所有被#include包含的头文件内容复制到源文件中,形成一个整体的源代码文件。
- 宏替换:将所有定义的宏进行替换,例如将所有出现的宏名替换为对应的宏定义内容。
- 条件编译:根据预处理指令(如#ifdef、#ifndef、#if、#elif等)的条件判断,选择性地编译代码段。
- 删除注释:删除所有的注释内容,包括单行注释(//)和多行注释(/* */)。
2. 编译(Compilation)
编译是将预处理后的源代码翻译成汇编语言的过程。编译器会对源代码进行词法分析、语法分析和语义分析,生成相应的中间代码。它主要包括以下几个步骤:
- 词法分析:将源代码划分为一个个的标记(token),如关键字、标识符、运算符等。
- 语法分析:根据语法规则,将标记组合成语法树,检查代码是否符合语法规范。
- 语义分析:对语法树进行语义检查,包括类型检查、变量声明检查等。
3. 汇编(Assembly)
汇编是将编译生成的中间代码翻译成机器指令的过程。汇编器将中间代码转换为机器码,并生成目标文件。它主要包括以下几个步骤:
- 符号解析:将变量和函数引用与其定义进行关联。
- 生成机器码:将汇编指令翻译成机器指令,生成目标文件。
4. 链接(Linking)
链接是将多个目标文件和库文件合并成一个可执行文件的过程。链接器将各个目标文件中的符号引用与其定义进行关联,解析符号引用,生成最终的可执行文件。它主要包括以下几个步骤:
- 符号解析:将各个目标文件中的符号引用与其定义进行关联。
- 重定位:将目标文件中的地址引用转换为实际的内存地址。
- 符号解析冲突处理:处理多个目标文件中相同符号的定义冲突。
以上就是C语言文件的四个编译过程的详细步骤。通过这四个过程,我们最终可以将源代码转换为可执行文件,并在计算机上运行。
举例
接下来进行举例验证上述四大步骤都是什么过程。
前置工作
main.c代码文件:
#include <stdio.h>
#define NUMBER 100
int main(int argc, char const *argv[])
{
printf("%d\n", NUMBER);
return 0;
}
第一步:预编译
gcc -E 文件名.c -o 文件名.i
此时看生成的".i"文件:
第二步:编译
gcc -S 文件名.i -o 文件名.s
此时看生成的".s"文件:
第三步:汇编
gcc -c 文件名.s -o 文件名.o
此时生成的".o"文件已经是二进制文件。
第四步:链接
gcc 文件名.o -o 可执行程序名
现象
所有步骤及运行结果如下图所示:
额外内容 —— ldd工具
当在Linux系统中编译和链接程序时,可能会涉及到动态链接库(Dynamic Linking),而 ldd
是一个非常有用的工具,它可以用于查看一个可执行文件或共享库所依赖的动态链接库。
ldd
命令的基本语法如下:
ldd [options] <executable_file>
<executable_file>
是可执行文件或共享库的路径,options
是可选参数,用于指定不同的输出格式或其他选项。
当你在终端中执行 ldd
命令时,它会列出可执行文件或共享库所依赖的动态链接库的路径。例如:
$ ldd /path/to/executable
ldd
命令的输出结果将显示出可执行文件或共享库所依赖的动态链接库路径,并且会标明是否找到了相应的库文件。如果找不到某个库文件,ldd
会显示 “not found”。
ldd
命令对于调试程序或解决库依赖问题非常有用。通过它,你可以查看一个程序所需要的动态链接库,以确保这些库文件存在且可用。如果 ldd
输出中显示某个库文件未找到,你可能需要安装相应的库文件或者调整库文件的搜索路径。
需要注意的是,ldd
只能用于查看动态链接库的依赖关系,对于静态链接库或静态链接的可执行文件,ldd
是无法显示其依赖关系的。