g++是GNU编译工具集(GCC)中的一个组件,用来编译C++语言写的源文件。
从C++源文件到可执行文件的编译过程,有如下几个步骤,g++提供了很多编译选项,可以让我们控制整个编译过程:
预编译(g++选项 -E)结果直接输出到控制台
编译 (g++选项 -S)结果保存为.s文件,汇编文件
汇编 (g++选项 -c)结果保存为.o文件,目标文件(object file)
链接 (g++选项 没有) 链接后结果就是可执行文件了
使用这些选项,我们就可以让编译在某一步结束之后停下来,输出那一步的结果。
编译既可以指整个从源文件到可执行文件的过程,也可以只是上面的第二步,大家注意通过上下文分辨。
预编译
这一步会把包含的头文件展开,定义的宏全部替换,比如下面这个例子:
#define ONE 1
#define TWO 2
int add_one_two(){
return ONE + TWO;
}
保存为add.cpp,使用命令g++ -E add.cpp编译,输出结果如下:
# 1 "add.cpp"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "add.cpp"
int add_one_two(){
return 1 + 2;
}
或许你会问,怎么没有#include,没有main函数啊?
没有#include是因为这个例子很简单,没有用到其他文件里面的声明或定义。没有main函数的原因是,那玩意是链接那一步才需要的,就好比我们做个炒鸡蛋是需要一个锅的,但是在打鸡蛋这一步只要个碗就好了。打鸡蛋相当于预编译,不需要锅;而炒鸡蛋就相当于链接,是需要一个锅的,输出就是可执行文件——炒好的鸡蛋了。
前面那些东西是#include <iostream>干的好事,预编译会把iosteam里面#include的文件和它自身的内容全部展开,这也算是个查看源文件的方法吧,而且看的仔细的话你会发现cout是一个模板类的实例(instance)。
编译
这一步是把预编译好的文件翻译成汇编语言,生成一个.s文件。是的,预编译后的文件还是人看的,汇编语言就已经是半兽人了;而整个编译过程确实是个翻译的过程——从高级语言到机器语言的翻译过程。发明一门新语言是个相反的过程,不过可以从汇编开始。
下面是个简单的求两个整数的和的函数:
-
int add(int a, int b){
-
return a + b;
-
}
使用命令g++ -S add.cpp,生成add.s,add.s内容如下:
-
.file "add.cpp"
-
.text
-
.globl _Z3addii
-
.type _Z3addii, @function
-
_Z3addii:
-
.LFB0:
-
.cfi_startproc
-
pushq %rbp
-
.cfi_def_cfa_offset 16
-
.cfi_offset 6, -16
-
movq %rsp, %rbp
-
.cfi_def_cfa_register 6
-
movl %edi, -4(%rbp)
-
movl %esi, -8(%rbp)
-
movl -8(%rbp), %eax
-
movl -4(%rbp), %edx
-
addl %edx, %eax
-
popq %rbp
-
.cfi_def_cfa 7, 8
-
ret
-
.cfi_endproc
-
.LFE0:
-
.size _Z3addii, .-_Z3addii
-
.ident "GCC: (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1"
-
.section .note.GNU-stack,"",@progbits
刚开始接触汇编确实会有些头大,不过看多了就好了。如果你想成为编程高手,那么汇编语言是必须要能看懂的,例子点击进入(例子一,例子二),不一定要能写,但一定要能看懂。
学汇编的好处还有:更好的理解C++中的指针和引用,理解函数调用,参数传递,理解栈、栈帧,等等,更多内容也是以后深入吧。迫不及待想了解汇编的朋友可以看看这篇博客,真的不难哦(猛击进入)。这个系列博客也不错,不过主题不是汇编(猛击进入)。
汇编
这一步也是翻译,把上一步的汇编语言的.s文件翻译成机器语言的.o文件,这个文件是个二进制文件,已经不是人看的了。想看的话当然有办法,可以使用类似objdump之类的工具反汇编查看。
生成的.o文件叫做目标文件,英语是object file,每个源文件(注意不是头文件哦)在编译过程中都会生成一个对应的目标文件,这样对于一个文件的反汇编调试会变得简单一点,因为目标文件经过最后一步(链接)后,通常会变得很大,就比较难调试了。
更深入点的内容可以参考我之前的一篇博客:)点我进入
链接
这一步是把上一步中生成的所有目标文件整合在一起,链接需要的库文件,好比现在要做蛋炒饭,汇编一步分别炒好了鸡蛋、蒸好了米饭,链接这一步就是把两个材料混在一起炒匀了,然后再加点调料(其他库文件)。具体内容也可以参考我那篇博客。
总结
当然g++提供的编译选项远远不止上面那几个,g++的帮助文件(man)的概要部分是这样的:
gcc [-c|-S|-E] [-std=standard]
[-g] [-pg] [-Olevel]
[-Wwarn...] [-pedantic]
[-Idir...] [-Ldir...]
[-Dmacro[=defn]...] [-Umacro]
[-foption...] [-mmachine-option...]
[-o outfile] [@file] infile...
Only the most useful options are listed here; see below for the
remainder. g++ accepts mostly the same options as gcc.
这篇博客介绍的内容只是第一个中括号里面的选项而已,后面的中括号里面还有各种其他选项,比较常用的有调试选项,警告选项,代码优化选项,-o选项。所以说g++是在是很强大,上手容易,要用好需要多练习。
放在中括号里的选项表示这些选项是可选的,只有后面infile是必须的,如果没有选项,比如我上一篇博客里面那样:g++ hello.cpp,g++会按照默认的选项编译出可执行文件,默认选项是什么我也不知道.
后面那句英语翻译过来(红色部分)是:只有比较有用的选项列了出来,剩下的在后面(我只贴了帮助文件的一小部分,后面还有很多没贴,大家用这个命令自己看吧:man g++,pageup,pagedown翻页)。g++接受和gcc基本类似的选项。(gcc是GNU的C语言的编译器)
我们在编译的整个过程中,会因为各种各样的问题,g++会给我们报各种错误。主要集中在编译和链接两步。
写程序时候的语法错误和声明问题会在编译这一步给出。定义问题会在链接这一步给出。声明和定义是两个问题,比较需要注意。
阅读(1093) | 评论(0) | 转发(0) |