C++编译过程详述

对于编译过程的总结,源于我的一个突然的好奇,编译头文件和编译头文件对应的源文件有没有区别?因为写makefile的时候依赖即可以写头文件,也可以写源文件。在网上查了半天,估计我的这个问题比较stupid,也没有明确的回答。索性顺藤摸瓜看了看C++的编译过程,虽然之前大致有个了解,但不够仔细。这一看发现,区别肯定是有的(尽量编译源文件而不要尝试编译源文件对应的头文件)。但更重要的是,把编译过程清楚捋一遍,解释了我之前不少的疑惑。学c++的时候上手就是vc6.0,对理解编译过程太不友好。所以还是应该有个全面的认识。(题外话,C++初学者最好用Linux开始学,不要用windows下的集成开发环境,很影响对于程序编译的理解)

1. 粗浅但实用的理解

总的来说,当你有一段代码后,编译器会帮你做两件大的事情,编译链接

编译的目的是将程序这种文本文件(比如.c文件)转变成计算机能读懂的目标文件(比如.o文件),目标文件全部由机器指令组成。

链接的目的是将多个编译生成的.o文件以及涉及到的库文件(.so或者.lib)结合,生成可执行文件(比如.out文件或者.exe文件)。

C++编译过程详述_第1张图片

对于编译器的编译过程,有了上面的粗浅理解,足以帮助理解程序在大部分情况下是如何运行的。虽然说是粗浅的理解,但是实用性其实非常强。

比如我最一开始的疑问,编译头文件和编译头文件对应的源文件有没有区别的。分开之后一步一步编译就会发现,单独编译源文件和单独编译头文件生成的结果是不同的。编译源文件会生成我们上面说的.o文件,编译头文件则会生成.gch文件。如果有这个文件在,再次改动头文件则无法更新,只有再该头文件被很多程序适用,且不怎么改动情况下,为了节省编译时间才会使用。如下图所示,我自己写了一个简单的add函数,放在add.cpp中,然后通过main.cpp调用它:

C++编译过程详述_第2张图片

另外,基于对上述两个大的过程的理解。在保证源文件正确的情况下,是一定可以编译生成一个目标文件的。(不用去管其他关联的文件是否正确)。所以如果对于编译多个源文件生成可执行文件报错,从两个大方向去分析即可。一是单独的源文件存在逻辑或语法的错误,二是链接多个源文件生成的目标文件时存在问题

2. 更细致的理解

编译过程当然没有你说的那么简单,但是我个人觉得上面的分析基本够用了。如果希望尝试更深入的理解,可以继续往下挖掘。

编译和链接这两件大事,可以继续细分。对于编译,主要可细分成三个阶段,预处理,编译(我知道这里又叫编译很让人混淆,但没办法习惯上就是这么说的--),汇编。每个步骤都会生成阶段性的文件,这些阶段性的过程文件在直接编译到可执行文件的结果中是不会展现的。大致过程是

首先是预处理阶段,生成的.i文件还是一个文本文件。可以用gcc -E生成。(单独使用gcc -E会把内容在屏幕上输出,可以使用gcc -E x.c -o x.i来输出到文件。)主要的功能是将所有#include头文件以及宏定义替换成真正的内容。下图中左边是.cpp文件,右边是.i文件。可以看到,头文件和我人为定义的宏cvalue都被替换了。引用的我们自己写的add.h头文件也被相应替换,add.h当中的真实内容被加入到了右边.i文件中。同时可以看到,.i文件大了许多,那是因为我们引用了标准的iostream,类比自己写的add.h,可以想见iostream当中的具体内容都被写到了.i文件里

C++编译过程详述_第3张图片

然后是编译阶段,生成的.s文件就是大名鼎鼎的汇编语言了。可以用gcc -S生成。这个阶段是最核心的阶段,包括词法,句法,语义分析。简单来说就是翻译,把你写的文本文件翻译成计算机语言随便看一小段,大家熟(陌)悉(生)的汇编语言吧。

C++编译过程详述_第4张图片

最后是汇编阶段,(不要误认为汇编阶段是生成汇编语言文件的),这个阶段是将汇编语言文件转变为目标文件——.o文件,这个文件就是由纯机器指令组成的了。可以用gcc -c生成。对于目标文件,其至少包括两段,即代码段和数据段。代码段主要是就是程序指令,数据段主要是全局变量或静态数据。

关于链接部分,没有再细分的子阶段了。但是要注意链接有两个概念,静态链接和动态链接。分别对应静态库和动态库的使用。静态链接是将程序的代码从静态库中拷贝到可执行文件中,而动态链接是将程序的代码的少量信息进行登记,不完全放到最终的可执行文件中。这个概念还挺重要的,后面找机会再详细演示。

你可能感兴趣的:(C++,Linux,c++,linux)