前言
C语言程序从源代码到二进制行程序都经历了那些过程?本文以Linux下C语言的编译过程为例,讲解C语言程序的编译过程。
编写hello world C程序:
(相关资料图)
// hello.c#includeint main(){ printf("hello world!\n");}
编译过程只需:
$ gcc hello.c # 编译$ ./a.out # 执行hello world!
这个过程如此熟悉,以至于大家觉得编译事件很简单的事。事实真的如此吗?我们来细看一下C语言的编译过程到底是怎样的。
上述gcc命令其实依次执行了四步操作:1.预处理(Preprocessing), 2.编译(Compilation), 3.汇编(Assemble), 4.链接(Linking)。
示例
为了下面步骤讲解的方便,我们需要一个稍微复杂一点的例子。假设我们自己定义了一个头文件mymath.h,实现一些自己的数学函数,并把具体实现放在mymath.c当中。然后写一个test.c程序使用这些函数。程序目录结构如下:
├── test.c└── inc ├── mymath.h └── mymath.c
程序代码如下:
// test.c#include#include "mymath.h"// 自定义头文件int main(){ int a = 2; int b = 3; int sum = add(a, b); printf("a=%d, b=%d, a+b=%d\n", a, b, sum);}
头文件定义:
// mymath.h#ifndef MYMATH_H#define MYMATH_Hint add(int a, int b);int sum(int a, int b);#endif
头文件实现:
// mymath.cint add(int a, int b){ return a+b;}int sub(int a, int b){ return a-b;}
1.预处理(Preprocessing)
预处理用于将所有的#include头文件以及宏定义替换成其真正的内容,预处理之后得到的仍然是文本文件,但文件体积会大很多。gcc的预处理是预处理器cpp来完成的,你可以通过如下命令对test.c进行预处理:
gcc -E -I./inc test.c -o test.i
或者直接调用cpp命令
$ cpp test.c -I./inc -o test.i
上述命令中-E是让编译器在预处理之后就退出,不进行后续编译过程;-I指定头文件目录,这里指定的是我们自定义的头文件目录;-o指定输出文件名。
经过预处理之后代码体积会大很多:
X文件名文件大小代码行数
预处理前test.c146B9
预处理后test.i17691B857
预处理之后的程序还是文本,可以用文本编辑器打开。
2.编译(Compilation)
这里的编译不是指程序从源文件到二进制程序的全部过程,而是指将经过预处理之后的程序转换成特定汇编代码(assembly code)的过程。编译的指定如下:
$ gcc -S -I./inc test.c -o test.s
上述命令中-S让编译器在编译之后停止,不进行后续过程。编译过程完成后,将生成程序的汇编代码test.s,这也是文本文件,内容如下:
// test.c汇编之后的结果test.s .file "test.c" .section .rodata.LC0: .string "a=%d, b=%d, a+b=%d\n" .text .globl main .type main, @functionmain:.LFB0: .cfi_startproc pushl %ebp .cfi_def_cfa_offset 8 .cfi_offset 5, -8 movl %esp, %ebp .cfi_def_cfa_register 5 andl $-16, %esp subl $32, %esp movl $2, 20(%esp) movl $3, 24(%esp) movl 24(%esp), %eax movl %eax, 4(%esp) movl 20(%esp), %eax movl %eax, (%esp) call add movl %eax, 28(%esp) movl 28(%esp), %eax movl %eax, 12(%esp) movl 24(%esp), %eax movl %eax, 8(%esp) movl 20(%esp), %eax movl %eax, 4(%esp) movl $.LC0, (%esp) call printf leave .cfi_restore 5 .cfi_def_cfa 4, 4 ret .cfi_endproc.LFE0: .size main, .-main .ident "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2" .section .note.GNU-stack,"",@progbits
请不要问我上述代码是什么意思!-_-
3.汇编(Assemble)
汇编过程将上一步的汇编代码转换成机器码(machine code),这一步产生的文件叫做目标文件,是二进制格式。gcc汇编过程通过as命令完成:
$ as test.s -o test.o
等价于:
gcc -c test.s -o test.o
这一步会为每一个源文件产生一个目标文件。因此mymath.c也需要产生一个mymath.o文件
4.链接(Linking)
链接过程将多个目标文以及所需的库文件(.so等)链接成最终的可执行文件(executable file)。
命令大致如下:
$ ld -o test.out test.o inc/mymath.o ...libraries...
结语
经过以上分析,我们发现编译过程并不像想象的那么简单,而是要经过预处理、编译、汇编、链接。尽管我们平时使用gcc命令的时候没有关心中间结果,但每次程序的编译都少不了这几个步骤。也不用为上述繁琐过程而烦恼,因为你仍然可以:
$ gcc hello.c # 编译$ ./a.out # 执行
近日,应急管理部印发了《矿山救护队标准化定级管理办法》(以下简称《定级办法》)。为落实好《定级办法》,现解读如下: 一、制定背更多
2023-01-11 10:07:48美国煤炭价格从历史高点大幅下跌,原因是暖冬天气缓解了对化石燃料需求。数据显示,截至1月6日当周,北部阿巴拉契亚地区的煤炭价格跌至每更多
2023-01-11 10:08:571月8日,2023年能源经济预测与展望研究系列报告发布。报告显示,2022年中国能源经济稳步回升,2023年能源经济走势向好。 最新发布的《更多
2023-01-11 09:57:50日前召开的全国矿山智能化建设和安全发展推进视频会透露,2020年八部委联合印发《关于加快煤矿智能化发展的指导意见》和全国煤矿智能化建更多
2023-01-11 10:12:061月6日,2022中国十大经济年度人物颁奖盛典在北京举行。中国梦杯•中国经济新闻人物——2022十大经济年度人物评选是由中国经济传媒协会主更多
2023-01-10 11:15:291月8日,船舶在黄骅港码头装运煤炭。黄骅港煤炭堆场转运设备在作业(1月8日摄,无人机照片)。 位于河北沧州的黄骅港是西煤东运、北煤南运更多
2023-01-10 10:12:461月7日,2022年煤矿智能化重大进展发布会在京召开。本次发布会旨在展示交流2022年煤矿智能化科技创新和建设成果,总结推广经验,发挥先进更多
2023-01-10 10:16:42记者从1月5日召开的2023年全区能源工作会议上获悉,2023年,我区将继续肩负起保障国家能源安全的重大政治责任,全力以赴保障能源安全稳定更多
2023-01-10 10:00:38据中国煤炭工业协会统计与信息部初步统计,2022年,全国原煤产量超5000万吨企业15家,与去年持平。产量合计约为259亿吨,较去年增加约13亿更多
2023-01-10 09:55:092022年,虽然受到国际能源市场持续紧张、高温干旱暴雨极端天气、疫情扰动以及输入性通胀等因素影响,但是在政策调控和市场机制的配合下,更多
2023-01-10 10:15:31