个人内容简介:
🍃个人主页:诉清风2023
🎈逆转时间的公式,就是珍惜现在ദ്ദി˶ー̀֊ー́ ) ✧
目录
前言
重点
在软件开发的过程中,从源代码到可执行程序的转变是一个复杂且有序的过程,通常包括预处理(Preprocessing)、编译(Compilation)、汇编(Assembly)和链接(Linking)几个阶段
注意
这部分内容并没有难度,只需要理解和会使用即可
1、预处理
补充:gcc格式
gcc [选项] 要编译的文件 [选项] [目标文件]
预处理是编译过程的第一步,它处理源代码文件中的预处理指令(以
#
开头的指令)。预处理器的任务主要包括:
- 宏定义(Macro Definition):处理
#define
指令,将宏名称替换为其定义的内容。- 条件编译(Conditional Compilation):通过
#ifdef
、#ifndef
、#endif
等指令,根据条件包含或排除代码段。- 文件包含(File Inclusion):通过
#include
指令将其他文件的内容插入到当前文件中。- 行控制和错误消息生成:使用
#line
指令控制编译器的错误和警告信息的行号,使用#error
和#warning
生成错误和警告消息。预处理后的结果是一个纯净的源代码文件,但其中已经包含了所有宏替换和文件包含的结果。
gcc –E code.c –o code.i
//选项“-E”,该选项的作用是让 gcc 在预处理结束后停止编译过程。
//选项“-o”是指目标文件,“.i”文件为已经过预处理的C原始程序。
2、编译
编译是将预处理后的源代码文件转换成汇编代码的过程。编译器的主要任务包括:
- 词法分析(Lexical Analysis):将源代码分解成一系列的标记(tokens)。
- 语法分析(Syntax Analysis):根据语言的语法规则分析标记,构建语法树(Syntax Tree)。
- 语义分析(Semantic Analysis):检查语法树中表达式的意义,进行类型检查等。
- 中间代码生成(Intermediate Code Generation):将语法树转换成一种中间表示(Intermediate Representation, IR),便于后续处理。
- 代码优化(Code Optimization):对中间代码进行优化,以提高代码执行效率。
- 目标代码生成(Target Code Generation):将优化后的中间代码转换成目标机器的汇编代码。
gcc –S code.i –o code.s
//选项“-S”,该选项的作用是让 gcc 在编译结束后停止汇编过程。
//选项“-o”是指目标文件,“.s”文件为已经过预处理和编译的C程序
3、汇编
汇编是将汇编代码转换成机器代码的过程。汇编器(Assembler)读取汇编代码,并将其转换为特定机器的机器语言指令。机器代码是计算机可以直接执行的代码。
gcc –C code.s –o code.o
//选项“-C”,该选项的作用是让 gcc 在汇编结束后停止链接过程。
//选项“-o”是指目标文件,“.o”文件为已经过预处理、编译和汇编的C程序
4、链接
链接是将多个编译后的机器代码文件(以及库文件)合并成一个可执行文件或库文件的过程。链接器的任务主要包括:
- 地址和空间分配 :为程序中的变量和函数分配内存地址。
- 符号解析(Symbol Resolution):解析程序中的符号引用,即确定程序中调用的函数或引用的变量在内存中的位置。
- 重定位(Relocation):修改代码和数据中的地址,以反映它们在内存中的最终位置。
- 生成可执行文件或库文件 :将多个对象文件合并,并可能包括一些运行时库,最终生成可执行文件或库文件。
这四个阶段共同构成了从源代码到可执行程序的完整开发流程。在现代开发环境中,这些步骤通常由构建系统(如Make、CMake、Maven等)自动化完成。
gcc code.o –o code
//选项“-o”是指目标文件,code为最后的可执行程序
5、gcc选项
- -E 只激活预处理,这个不生成文件,你需要把它重定向到一个输出文件里面
- -S 编译到汇编语言不进行汇编和链接
- -c 编译到目标代码
- -o 文件输出到文件
- -static 此选项对生成的文件采用静态链接
- -g 生成调试信息。GNU 调试器可利用该信息。
- -shared 此选项将尽量使用动态库,所以生成文件比较小,但是需要系统由动态库.
- -O0
- -O1
- -O2
- -O3 编译器的优化选项的4个级别,-O0表示没有优化,-O1为缺省值,-O3优化级别最高
- -w 不生成任何警告信息。
- -Wall 生成所有警告信息。
6、函数库
我们的C程序中,并没有定义“printf”的函数实现,且在预编译中包含的“stdio.h”中也只有该函数的声明,而没有定义函数的实现,那么,是在哪里实“printf”函数的呢?
最后的答案是:系统把这些函数实现都被做到名为 libc.so.6 的库文件中去了,在没有特别指定时,gcc 会到系统默认的搜索路径“/usr/lib”下进行查找,也就是链接到 libc.so.6 库函数中去,这样就能实现函数“printf”了, 而这也就是链接的作用。
6.1 静态库
静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就 不再需要库文件了。
其后缀名一般为“.a”
注意:
如果在编译过程中敲静态编译的指令报错后,检查是否是因为未安装静态库
静态库安装方法:
sudo yum -y install glibc-static //静态库安装方法 gcc -o code_static code.c -static //静态库编译链接
6.2 动态库
动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以节省系统的开销。
动态库一般后缀名为“.so”。
gcc 在编译时默认使用动态库。完成了链接之后,gcc 就可以生成可执行文件,如下所示。
gcc code.o –o code gcc
默认生成的二进制程序,是动态链接的,这点可以通过 file 命令验证。
结语
这部分知识其实也并不多,了解.c文件变为可执行程序的过程即可,动态与静态库这里只作为提及,后续学习过程中我们会加深回他的理解,等今后的我们能力充足之后便可以熟知!