当前位置:文档之家› gcc入门教程

gcc入门教程

不经意间,GCC已发展到了4.3的版本,尽管在软件开发社区之外乏人闻问,但因为GCC 在几乎所有开源软件和自由软件中都会用到,因此它的编译性能的涨落会直接影响到Linux 、Firefox 乃至于和Apache等几千个项目的开发。

因此,把GCC摆在开源软件的核心地位是一点也不为过。

另一方面,GCC4.3的出现,正在牵引着广大程序员们的心。

如果我们非要用一个词来说明GCC与程序员之间的关系,那无疑是"心随心动"。

历史篇作为自由软件的旗舰项目,Richard Stallman 在十多年前刚开始写作GCC 的时候,还只是把它当作仅仅一个 C 程序语言的编译器;GCC 的意思也只是GNU C Compiler 而已。

经过了这么多年的发展,GCC 已经不仅仅能支持C 语言;它现在还支持Ada 语言、C++ 语言、Java 语言、Objective C 语言、Pascal 语言、COBOL语言,以及支持函数式编程和逻辑编程的Mercury 语言,等等。

而GCC 也不再单只是GNU C 语言编译器的意思了,而是变成了GNU Compiler Collection 也即是GNU 编译器家族的意思了。

另一方面,说到GCC 对于各种硬件平台的支持,概括起来就是一句话:无所不在。

几乎所有有点实际用途的硬件平台,甚至包括有些不那么有实际用途的硬件平台。

Gcc 简介Linux系统下的gcc(GNU C Compiler)是GNU推出的功能强大、性能优越的多平台编译器,是GNU的代表作品之一。

Gcc是可以在多种硬体平台上编译出可执行程序的超级编译器,其执行效率与一般的编译器相比平均效率要高20%~30%。

官方网站:/gcc是linux的唯一编译器,没有gcc就没有linux,gcc的重要性就不可言喻啦。

居然这么重要,那就很值得我们来好好研究下啦。

好啦,开始我们的gcc之旅吧!首先消除gcc和g++误区吧。

gcc和g++都是GNU(组织)的一个编译器。

误区一:gcc只能编译c代码,g++只能编译c++代码两者都可以,但是请注意:1.后缀为.c的,gcc把它当作是C程序,而g++当作是c++程序;后缀为.cpp的,两者都会认为是c++程序,注意,虽然c++是c的超集,但是两者对语法的要求是有区别的,例如:#include <stdio.h>int main(int argc, char* argv[]) {if(argv == 0) return;printString(argv);return;}int printString(char* string) {sprintf(string, "This is a test.\n");}如果按照C的语法规则,OK,没问题,但是,一旦把后缀改为cpp,立刻报三个错:“printString 未定义”;“cannot convert `char**' to `char*”;”return-statement with no value“;分别对应前面红色标注的部分。

可见C++的语法规则更加严谨一些。

2.编译阶段,g++会调用gcc,对于c++代码,两者是等价的,但是因为gcc命令不能自动和C++程序使用的库联接,所以通常用g++来完成链接,为了统一起见,干脆编译/链接统统用g++了,这就给人一种错觉,好像cpp程序只能用g++似的。

误区二:gcc不会定义__cplusplus宏,而g++会实际上,这个宏只是标志着编译器将会把代码按C还是C++语法来解释,如上所述,如果后缀为.c,并且采用gcc编译器,则该宏就是未定义的,否则,就是已定义。

误区三:编译只能用gcc,链接只能用g++严格来说,这句话不算错误,但是它混淆了概念,应该这样说:编译可以用gcc/g++,而链接可以用g++或者gcc -lstdc++。

因为gcc命令不能自动和C++程序使用的库联接,所以通常使用g++来完成联接。

但在编译阶段,g++会自动调用gcc,二者等价。

误区四:extern "C"与gcc/g++有关系实际上并无关系,无论是gcc还是g++,用extern "c"时,都是以C的命名方式来为symbol 命名,否则,都以c++方式命名。

试验如下:me.h:extern "C" void CppPrintf(void);me.cpp:#include <iostream>#include "me.h"using namespace std;void CppPrintf(void){cout << "Hello\n";}test.cpp:#include <stdlib.h>#include <stdio.h>#include "me.h"int main(void){CppPrintf();return 0;}1. 先给me.h加上extern "C",看用gcc和g++命名有什么不同[root@root G++]# g++ -S me.cpp[root@root G++]# less me.s.globl _Z9CppPrintfv //注意此函数的命名.type CppPrintf, @function[root@root GCC]# gcc -S me.cpp[root@root GCC]# less me.s.globl _Z9CppPrintfv //注意此函数的命名.type CppPrintf, @function完全相同!2. 去掉me.h中extern "C",看用gcc和g++命名有什么不同[root@root GCC]# gcc -S me.cpp[root@root GCC]# less me.s.globl _Z9CppPrintfv //注意此函数的命名.type _Z9CppPrintfv, @function[root@root G++]# g++ -S me.cpp[root@root G++]# less me.s.globl _Z9CppPrintfv //注意此函数的命名.type _Z9CppPrintfv, @function完全相同!【结论】完全相同,可见extern "C"与采用gcc/g++并无关系,以上的试验还间接的印证了前面的说法:在编译阶段,g++是调用gcc的。

二:今天,我们继续gcc之旅吧。

上节我们讲了些gcc的历史发展什么的,还有就是gcc与g++的区别。

今天我们就从整体上对gcc编译过程有个细致的了解,也好明白他的工作原理,好为以后深入学习研究打下个基础。

gcc的编译流程分为四个步骤,分别为:·预处理(Pre-Processing)·编译(Compiling)->hello.i·汇编(Assembling)->hello.s·链接(Linking)以hello.c为例子,在这四个步骤中可以设置选项分别生成hello.i, hello.s, hello.o以及最终的hello文件:hello.c : 最初的源代码文件;hello.i : 经过编译预处理的源代码;hello.s : 汇编处理后的汇编代码;hello.o : 编译后的目标文件,即含有最终编译出的机器码,但它里面所引用的其他文件中函数的内存位置尚未定义。

hello / a.out : 最终的可执行文件(还有.a(静态库文件), .so(动态库文件), .s(汇编源文件)留待以后讨论)下面就具体来查看一下gcc是如何完成四个步骤的。

hello.c源代码#include<stdio.h>int main(){printf("Hello World!\n");return 0;}(1)预处理阶段在该阶段,编译器将上述代码中的stdio.h编译进来,并且用户可以使用gcc的选项”-E”进行查看,该选项的作用是让gcc在预处理结束后停止编译过程。

《深入理解计算机系统》中是这么说的:预处理器(cpp)根据以字符#开头的命令(directives),修改原始的C程序。

如hello.c中#include <stdio.h>指令告诉预处理器读系统头文件stdio.h的内容,并把它直接插入到程序文本中去。

结果就得到另外一个C程序,通常是以.i作为文件扩展名的。

注意:Gcc指令的一般格式为:Gcc [选项] 要编译的文件[选项] [目标文件]其中,目标文件可缺省,Gcc默认生成可执行的文件名为:编译文件.out[gan@localhost gcc]# gcc –E hello.c –o hello.i选项”-o”是指目标文件,”.i”文件为已经过预处理的C原始程序。

以下列出了hello.i文件的部分内容:typedef int (*__gconv_trans_fct) (struct __gconv_step *,struct __gconv_step_data *, void *,__const unsigned char *,__const unsigned char **,__const unsigned char *, unsigned char **,size_t *);…# 2 "hello.c" 2int main(){printf("Hello World!\n");return 0;}由此可见,gcc确实进行了预处理,它把”stdio.h”的内容插入到hello.i文件中。

(2)编译阶段接下来进行的是编译阶段,在这个阶段中,Gcc首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,Gcc把代码翻译成汇编语言。

用户可以使用”-S”选项来进行查看,该选项只进行编译而不进行汇编,生成汇编代码。

汇编语言是非常有用的,它为不同高级语言不同编译器提供了通用的语言。

如:C编译器和Fortran 编译器产生的输出文件用的都是一样的汇编语言。

[gan@localhost gcc]# gcc –S hello.i –o hello.s以下列出了hello.s的内容,可见Gcc已经将其转化为汇编了,感兴趣的读者可以分析一下这一行简单的C语言小程序是如何用汇编代码实现的。

.file "hello.c".section .rodata.align 4.LC0:.string "Hello World!".text.globl main.type main, @functionmain:pushl %ebpmovl %esp, %ebpsubl $8, %espandl $-16, %espmovl $0, %eaxaddl $15, %eaxaddl $15, %eaxshrl $4, %eaxsall $4, %eaxsubl %eax, %espsubl $12, %esppushl $.LC0call putsaddl $16, %espmovl $0, %eaxleaveret.size main, .-main.ident "GCC: (GNU) 4.0.0 20050519 (Red Hat 4.0.0-8)".section .note.GNU-stack,"",@progbits(3)汇编阶段汇编阶段是把编译阶段生成的”.s”文件转成目标文件,读者在此可使用选项”-c”就可看到汇编代码已转化为”.o”的二进制目标代码了。

相关主题