本文最后更新于 405 天前,其中的信息可能已经有所发展或是发生改变。
前言
C语言是编译性的语言,所以说C语言程序要运行有一个必不可少的过程——编译,这篇笔记解释了我对C语言编译过程的理解
c语言编译过程大概分为以下几个流程,预处理=》编译=》=汇编=》链接,接下来就会详细分析一下上面的过程
预处理
简单来说,C语言的预处理的过程有点类似于文本的替换,即将源代码文件中的#开头的命令来进行替换,比如将 #include替换为其真正的内容,预处理之后源代码文件仍然还是一个文本文件,只不过比之前的文件大的多了。一般找不到库文件的问题会在这个阶段进行报错
编译
接下来就是重头戏了,编译阶段。在这个阶段,编译器首先先来检查程序中是否有语法错误(不是逻辑错误哦!),如果有的话便抛出错误,没有的话便会将预处理器生成的代码将被转化为对应的汇编代码,注意注意,这里的汇编代码我们人仍然能够勉强看懂。以下举个小例子
首先假设有一个C语言程序,源代码如下,文件名被命名为了 1.c
int main()
{
int i = 0;
printf("i = %d",i);
return 0;
}
然后在编译过程中加入-S参数(使用gcc编译),让gcc在编译到汇编过程就停止,然后就会在1.c文件的旁边出现了一个叫做1.s的文件,这个文件的内容如下(运行环境ubuntu 22.04)
.file "1.c"
.text
.section .rodata
.LC0:
.string "i = %d"
.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 $0, -4(%rbp)
movl -4(%rbp), %eax
movl %eax, %esi
leaq .LC0(%rip), %rax
movq %rax, %rdi
movl $0, %eax
call printf@PLT
movl $0, %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0"
.section .note.GNU-stack,"",@progbits
.section .note.gnu.property,"a"
.align 8
.long 1f - 0f
.long 4f - 1f
.long 5
0:
.string "GNU"
1:
.align 8
.long 0xc0000002
.long 3f - 2f
2:
.long 0x3
3:
.align 8
4:
以上就是汇编的代码
汇编
这个阶段,编译器便将刚刚“编译”阶段产生的汇编代码翻译成机器所能理解的2进制文件
链接
链接过程中编译器将多个源代码文件以及库文件(.lib或.so)链接在一起,生成出最终的结果.out文件或者.exe文件