公告:本站提供编程开发方面的技术交流与分享,打造最佳教程网,希望能为您排忧解难!

C语言教程GCC入门详解

GCC入门详解

更新时间:2013-04-16 00:17:08 |

作为自由软件的旗舰项目,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 对于操作系统平台及硬件平台支持,概括起来就是一句话:无所不在。

1 程序编译过程

--------------------------------------------------------------------------------
  GCC是CUI(命令行交互界面)程序,这让许多从Windows走出来 Guier们感到恐惧。实际上它也有许多前端窗口界面,Windows下有Dev C++,Linux下譬如KDevelopment,但既然选择了GCC还是将CUL进行到底吧,没有难与不难的问题,只有做与不做的问题!

  下面基于一个具体而微的程序,讨论GCC的使用。示例程序如下:


//test.c
#include <stdio.h>
int main(void)
{
  printf("Hello World!\n");
  return 0;
}

 

  这个程序,一步到位的编译指令是:

gcc test.c -o test


  输出的可执行文件名为test,Windows用户可能会感到奇怪,可执行文件明怎么没有.exe扩展名呢?Linux系统中,文件类型并非以扩展名识别的!

  实质上,上述编译过程是分为四个阶段进行的,即预处理(也称预编译,Preprocessing)、编译(Compilation)、汇编 (Assembly)和连接(Linking)。

1.1 预处理
  运行预处理命令:

gcc -E test.c -o test.i 或 gcc -E test.c

可以输出test.i文件中存放着test.c经预处理之后的代码。打开test.i文件,看一看,就明白了。后面那条指令,是直接在命令行窗口中输出预处理后的代码,而不是以文件作为输出设备。gcc的-E选项,可以让编译器在预处理后停止,并输出预处理结果。在本例中,预处理结果就是将stdio.h 文件中的内容插入到test.c中了。

  gcc的-o选项,用于输出处理结果到文件中。

1.2 编译为汇编代码
  预处理之后,可直接对生成的test.i文件编译,生成汇编代码:

gcc -S test.i -o test.s


  gcc的-S选项,表示在程序编译期间,在生成汇编代码后,停止,-o输出汇编代码文件。

  生成的汇编代码如下:

    .file    "test.c"
    .section    .rodata
    .align 4
.LC0:
    .string    "Hello World,Linux programming!"
    .text
.globl main
    .type    main, @function
main:
    leal    4(%esp), %ecx
    andl    $-16, %esp
    pushl    -4(%ecx)
    pushl    %ebp
    movl    %esp, %ebp
    pushl    %ecx
    subl    $4, %esp
    movl    $.LC0, (%esp)
    call    puts
    movl    $0, %eax
    addl    $4, %esp
    popl    %ecx
    popl    %ebp
    leal    -4(%ecx), %esp
    ret
    .size    main, .-main
    .ident    "GCC: (GNU) 4.1.0 20060304 (Red Hat 4.1.0-3)"
    .section    .note.GNU-stack,"",@progbits

1.3 汇编(Assembly)
  如果你学过汇编语言,那么你就该知道程序编译到了这个地步,应当使用汇编器,将汇编语言翻译为机器代码了。这一步尤其重要,因为它决定了你生成的程序,能够运行在哪种机器上。gcc使用的汇编器是gas。

  在Intel IA-32平台上,还有一些常用的汇编器有:

微软的MASM,这是Intel平台上所有汇编器的鼻祖了,它现在已不是微软的独立产品,只是与Visual Studio捆绑在一起。但微软允许其他组织免费分发MASM 6.0。
NASM,最初是为UNIX环境开发的商业汇编器,最近成为开源的了,可生成UNIX、MS-DOS和32位Windows格式的可执行文件。
HLA(high level assembler)是Randall Hyde教授创建的,可以在DOS、Windows和Linux操作系统上生成Intel指令码。但HLA设计的主要目的是向初级程序员讲授汇编语言,学院气太浓,不够实用。
  与这些汇编器相比,gas可以在不同处理器平台上工作,通常它可以自动检测底层硬件平台并生成适合该平台的正确机器指令码。gas另一个特性是能够创建不同于程序设计所在平台的指令码,譬如我在Intel计算机上工作,但可以为MIPS计算机写程序。

  对于上一小节中生成的汇编代码文件test.s,gas汇编器负责将其编译为目标文件,如下:

gcc -c test.s -o test.o


1.4 连接
  gcc连接器是gas提供的,负责将程序的目标文件与所需的所有附加的目标文件连接起来,最终生成可执行文件。附加的目标文件包括静态连接库和动态连接库。

  对于上一小节中生成的test.o,将其与C标准输入输出库进行连接,最终生成程序test:

gcc test.o -o test


  在命令行窗口中,运行test这个小程序,让它说HelloWorld吧!


2、多个程序文件的编译

--------------------------------------------------------------------------------

  通常整个程序是由多个源文件组成的,相应地也就形成了多个编译单元,使用GCC能够很好地管理这些编译单元。假设有一个由test1.c和 test2.c两个源文件组成的程序,为了对它们进行编译,并最终生成可执行程序test,可以使用下面这条命令:

# gcc test1.c test2.c -o test


  如果同时处理的文件不止一个,GCC仍然会按照预处理、编译和链接的过程依次进行。如果深究起来,上面这条命令大致相当于依次执行如下三条命令:

# gcc -c test1.c -o test1.o
# gcc -c test2.c -o test2.o
# gcc test1.o test2.o -o test


  需要打这么多编译指令,看着都累,许多Guier们又要抱怨了。的确如此,如果单单使用GCC来编译你的程序,一千个程序源文件的项目编译至少要在命令行窗口中敲1k次文件名,才能完成一次编译。如果代码有了改动,重新编译,需要再原样输入一次编译指令。再技术高超的Cler也会累死的,但是很奇怪,那些Cler们至今依然活的很生龙活虎,这得益于GNU Make工具,详情见Make基础一节。


3、检错

--------------------------------------------------------------------------------

  GCC包含完整的出错检查和警告提示功能,可以帮助程序员写出更为标准、健壮的代码。如下面的代码:

//illcode.c
#include <stdio.h>
void main(void)
{
  long long int var = 1;
  printf("It is not standard C code!\n");
  printf("long long int var=%d",var);
}


  这种代码,可能在老的C语言课本里能够见到,但它是不符合ANSI/ISO C语言标准的。我让同学在Visual Stdio .net 2003上编译了一下,没检测出什么问题来。下面看看GCC可不可以:

gcc -pedantic illcode.c -o illcode


  输出结果:

illcode.c: 在函数 ‘main’ 中:
illcode.c:5: 警告:ISO C90 不支持 ‘long long’
illcode.c:4: 警告:‘main’ 的返回类型不是 ‘int’


  -pedantic编译选项并不能保证被编译程序与ANSI/ISO C标准的完全兼容,它仅仅只能用来帮助Linux程序员离这个目标越来越近。或者换句话说,-pedantic选项能够帮助程序员发现一些不符合 ANSI/ISO C标准的代码,但不是全部,事实上只有ANSI/ISO C语言标准中要求进行编译器诊断的那些情况,才有可能被GCC发现并提出警告。

  如果采用默认的编译,即:gcc -pedantic illcode.c -o illcode。输出:

test.c: 在函数 ‘main’ 中:
test.c:4: 警告:‘main’ 的返回类型不是 ‘int’


  上面的示例中,long long int是GNU C的扩展类型,表示64位整型数,这种类型没有纳入C/C++标准中,可见GCC默认的编译指令,无法完全检测出不符合标准C/C++的代码,但要比 Visual Stdio .net 2003一声都不吭要好一些。如果使用-pedantic选项,GCC就可以基本上按照标准C/C++进行代码检测了,不要挑剔什么,迄今为止没有任何一款编译器完全支持标准C/C++的。
  
  除了-pedantic之外,GCC还有一些其它编译选项也能够产生有用的警告信息。这些选项大多以-W开头,其中最有价值的当数-Wall了,使用它能够使GCC产生尽可能多的警告信息。

  GCC给出的警告信息虽然从严格意义上说不能算作错误,但却很可能成为错误的栖身之所。一个优秀的Linux程序员应该尽量避免产生警告信息,使自己的代码始终保持标准、健壮的特性。所以将警告信息当成编码错误来对待,是一种值得赞扬的行为!所以,在编译程序时带上-Werror选项,那么GCC会在所有产生警告的地方停止编译,迫使程序员对自己的代码进行修改,如下:

gcc -Werror test.c -o test

 
  输出:

cc1: warnings being treated as errors
test.c: 在函数 ‘main’ 中:
test.c:4: 警告:‘main’ 的返回类型不是 ‘int’

最佳教程网

最大的技术交流平台 www.goodxyx.com© CopyRight 2011-2013, All Rights Reserved

浙ICP备11033019号