参考:
http://blog.csdn.net/xiaohouye/article/details/52084770
https://www.cnblogs.com/wuyouxiaocai/p/5701088.html
https://www.cnblogs.com/ziyunlong/p/6023121.html
我们那一个简单的程序举例说明:
文件 hello.cpp:
#include
int main()
{
prinftf("hello world! \n");
return 0;
}
命令:
g++ hello.cpp -o hello.ii
预处理过程实质上是处理“#”,将#include包含的头文件直接拷贝到hell.cpp当中;将#define定义的宏进行替换,同时将代码中没用的注释部分删除等
具体做的事儿如下:
(1)将所有的#define删除,并且展开所有的宏定义。说白了就是字符替换
(2)处理所有的条件编译指令,#ifdef #ifndef #endif等,就是带#的那些
(3)处理#include,将#include指向的文件插入到该行处
(4)删除所有注释
(5)添加行号和文件标示,这样的在调试和编译出错的时候才知道是是哪个文件的哪一行
(6)保留#pragma编译器指令,因为编译器需要使用它们。
我们可以打开hello.ii文件,查看其内容:
我们发现生成的hello.ii文件有上万行,里面的内容与其引入的标准库里的文件一致,只是去处了注释,翻译了‘#’。
可以发现预处理是将程序所需要的所有头文件都包含到了“中间文件”hello.ii中。
命令:
g++ –S hello.ii –o hello.s
参数是-S生成可查看的文本文件,-s生成二进制文件
编译的过程实质上是把高级语言翻译成机器语言的过程,即对hello.cpp做了这些事儿:
(1)词法分析
(2)语法分析
(3)语义分析
(4)优化后生成相应的汇编代码
从 高级语言->汇编语言。
- 具体,参考 1
命令:
g++ –c hello.s –o hello.o
汇编是吧汇编语言->机器语言(二进制)即将源文件翻译成二进制文件。类Uinx系统编译的结果生生成.o文件,Windows系统是生成.obj文件。
源代码和最终目标文件中过渡的就是中间代码.obj,实际上之所以需要中间代码,是你不可能一次得到目标文件。比如说一个exe需要很多的cpp文件生成。而编译器一次只能编译一个cpp文件。这样编译器编译好一个cpp以后会将其编译成obj,当所有必须要的cpp都编译成obj以后,再统一link成所需要的exe,应该说缺少任意一个obj都会导致exe的链接失败(window下)。
举一反三:
C++程序的每个cpp文件都会生成.o文件,生成.o文件需要预编译、编译、汇编的过程。当我们只修改其中的一个cpp文件时,只需重新编译该cpp文件即可,但如果我们修改h文件,由于可能有多个cpp文件引入了该h文件,该多个cpp文件都需要重新生成.o文件。
命令:
g++ –c hello.s –o hello.o
hello.cpp它使用到了C标准库的函数“printf”,但是编译过程只是把源文件翻译成二进制而已,每个库文件和cpp生成的.o只是独立的个体,将它们需要绑定在一起即链接。
我们平时口中的编译,其实就是上面的四个过程的集合,在实际编译中,不需要生成那么多中间文件,只需要下面命令:
g++ hello.cpp –o hello
CMake、Qmake等工具其实做的就是组织源文件(.h .cpp)的位置和位置关系,和标识非C++标准库的库文件的位置,为整个程序提供编译器参数,使编译器完成预处理,编译,汇编和链接的工作。
[1]编译过程细节
编译程序的工作过程一般划分为五个阶段:词法分析、语法分析、语义分析与中间代码产生、优化、目标代码生成。
词法分析:也就是从左到右一个一个的读入源程序,识别一个单词或符号,并进行归类。
语法分析: 在词法分析的基础上,将单词序列分解成各类语法短语,如“程序”,“语句”,“表达式”等。
语义分析:审查源程序是否有语义的错误,当不符合语言规范的时候,程序就会报错。
中间代码生成:在进行了语法和语义的分析工作之后,编译程序将源程序变成了一种内部表示形式,这种内部表示形式叫做中间语言或中间代码。
代码优化:这个阶段是对前阶段的中间代码进行变换或改造,目的是使生成的目标代码更为高效,即节省时间和空间。
目标代码生成:也就是把优化后的中间代码变换成指令代码或汇编代码。