转自:http://blog.csdn.net/high_high/article/details/7274541
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函数的原因是,那玩意是链接那一步才需要的,就好比我们做个炒鸡蛋是需要一个锅的,但是在打鸡蛋这一步只要个碗就好了。打鸡蛋相当于预编译,不需要锅;而炒鸡蛋就相当于链接,是需要一个锅的,输出就是可执行文件——炒好的鸡蛋了。
怎么样,很好玩吧。
当然那两个东西也可以有,给大家一个练习吧,把上一篇博客里面那个hello world程序用这个步骤实验一下,结果太长我不贴出来了,不过你一定要试一下阿,一定会吓你一跳的。
前面那些东西是#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++会给我们报各种错误。主要集中在编译和链接两步。
写程序时候的语法错误和声明问题会在编译这一步给出。定义问题会在链接这一步给出。声明和定义是两个问题,比较需要注意,我以后再深入了。
好了,g++暂时介绍到这吧,最后说一点题外话。
从我写上上一篇博客《几点学习linux编程的建议》写到最后发现自己离题万里开始,我就知道我需要写个系列了,系列的主题就是那篇博客的第一句话:”我个人用了3、4年linux,从当初的小菜鸟到现在的大菜鸟,积累了一些经验,现在分享给大家,尤其是初学者。“
这里的初学者主要是大学一二三四年级的学生朋友,包括没有编程经验到只有windows编程经验学生朋友。个人觉得吧,windows编程是个舒服的牢笼,linux编程是充满风雨的世界。再舒服的牢笼也是牢笼,总有一天总会让你觉得自己是被束缚的;而在风雨的世界里面,你却可以建筑一个比牢笼舒服百倍的自己的城堡。通过这个系列,我希望我能帮助初学者朋友在linux的世界里面搭个自己的小木屋,至于建城堡,就看各位的想象力和创造力了。
最后,希望大家多动手,多练习。如果不知道拿什么练习,请参考我上一篇博客:)
祝你好运。