基于示例C++程序的编译流程解析及现有认知验证咨询
用一段简单C++代码理解编译流程
我最近在通过这段C++代码拆解编译的完整流程,帮自己搞清楚程序从源码到可执行文件到底经历了哪些步骤:
#include<iostream.h> #include<conio.h> #include<math.h> #define sum(a,b) a+b int a; void main() { int b; cin>>a>>b; cout<<"hello"<<endl; cout<<pow(a,2)<<endl; cout<<sum(a,b); getch(); }
1. 预处理阶段
这是编译的第一步,主要处理源码中所有以#开头的预编译指令,我自己梳理了几个关键操作:
- 宏展开:代码里的
#define sum(a,b) a+b会做纯文本替换,把所有出现sum(a,b)的地方直接换成a+b。比如代码里的cout<<sum(a,b);会变成cout<<a+b;。这里要注意,宏只是简单替换,不会做语法分析,如果写成sum(a,b)*2,结果会是a+b*2,不是我们预期的(a+b)*2,这也是宏容易踩坑的地方。 - 头文件包含:
#include指令会把指定头文件的内容完整插入到当前源码中。比如iostream.h里的cin、cout的定义,math.h里pow()的函数原型,conio.h里getch()的声明,都会被直接拷贝到我们的代码里,这样编译器才能识别这些函数和对象。 - 另外,全局变量
int a;这类普通代码在预处理阶段不会被修改,预处理只处理预编译相关的指令。
2. 编译阶段
预处理完成后,编译器会把处理后的代码转换成汇编语言,同时做全面的语法检查。比如如果我把cin写成cinx,编译器就会在这里报错说“未定义标识符”;如果调用pow时传了字符串参数,也会在这里提示类型不匹配。
另外我注意到,代码里的void main()其实不符合标准C++的规范,标准里要求main函数返回int类型,不过一些老的编译器还是支持这种写法,编译阶段可能不会报错,但严格来说应该改成int main()并加上return 0;。
3. 汇编阶段
这一步会把上一步生成的汇编代码转换成机器能直接识别的二进制目标文件(通常是.obj或.o格式)。每个源文件都会生成一个独立的目标文件,里面包含了代码对应的机器指令,但还不是完整的可执行程序。
4. 链接阶段
最后一步就是链接,链接器会把我们的目标文件和程序依赖的标准库(比如实现cout、pow这些功能的库文件)链接在一起,生成完整的可执行文件。比如pow()函数的实际实现代码并不在我们的源码里,而是在数学标准库中,链接器会找到这个函数的实现并把它合并到最终的可执行文件中;getch()也是同理,来自conio库的实现。
内容的提问来源于stack exchange,提问作者Meenie Leis
相关产品推荐
相关产品推荐

