C 语言的编译过程包括了预处理、编译、汇编和链接四个步骤,下面对这四个步骤进行详细解释。
1.预处理
在编译之前,C 语言编译器需要先对源代码进行预处理。预处理器读取源代码并进行文本替换、条件编译等操作,生成一个经过预处理的源文件,例如:
#include <stdio.h>
#define PI 3.1415926
int main()
{
int r = 2;
float area;
area = PI * r * r;
printf("Area of the circle: %f\n", area);
return 0;
}
经过预处理之后,该代码会变成:
# 1 "test.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "test.c"
# 1 "/usr/include/stdio.h" 1 3 4
# 27 "/usr/include/stdio.h" 3 4
......
int main()
{
int r = 2;
float area;
area = 3.1415926 * r * r; // 宏定义已经替换
printf("Area of the circle: %f\n", area);
return 0;
}
在这个阶段,预处理器主要做以下事情:
处理预编译指令,如宏定义、条件编译等;
包含头文件;
删除注释;
等。
2.编译
在预处理完成之后,C 语言编译器将会编译经过预处理的源文件,并输出汇编代码。编译器会检查代码的语法、类型等错误,并将高级语言代码转换为汇编代码。
在上述例子中,经过编译器处理之后,会生成一个汇编代码文件,例如:
.file "test.c"
.text
.section .rodata
.LC0:
.string "Area of the circle: %f\n"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movl $2, -4(%rbp)
fldpi
fildl -4(%rbp)
fmul
fmul
fstps -8(%rbp)
movl $.LC0, %eax
movss -8(%rbp), %xmm1
movl $1, %esi
movq %xmm1, %xmm0
call printf
movl $0, %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 9.3.0-10ubuntu2) 9.3.0"
.section .note.GNU-stack,"",@progbits
3.汇编
汇编器接收编译器生成的汇编代码,并将其转化为可执行机器码。在汇编阶段中,将会对代码进行一系列的优化,生成一种可以被计算机所理解的指令。
4.链接
链接器将多个目标文件和库文件链接成一个可执行文件。在链接阶段中,链接器将目标文件中未定义的函数或变量链接到其他目标文件或库文件中已经定义的函数或变量上。如果在链接阶段中遇到了未定义的函数或变量,则会报链接错误。
最后生成一个可执行的exe文件。
经过编译、汇编和链接的机器代码和数据,可以被操作系统直接加载并执行
流程:
C 语言的编译过程一般可以分为四个步骤:预处理、编译、汇编和链接。
首先,预处理器读取源代码并进行文本替换、条件编译等操作,得到经过预处理的源代码。
其次,编译器将经过预处理的源代码转化为汇编代码,生成汇编文件。
第三步,汇编器将汇编代码转化为机器码,生成目标文件。
最后,链接器将目标文件与库文件进行链接,生成可执行文件。
这个工作流程可以叠加多次,因为在链接过程中可能需要链接多个目标文件和库文件。