GCC编译器总结

作者: Manfred_Zone | 来源:发表于2016-04-20 17:17 被阅读1552次

    GCC简介

    1、GCC经过那么多年的发展,已经从最初的C编译器转变成了编译器的集合,官方定义是GNU Complier Collection,现在的GCC不仅支持C还支持C++、Java等语言。
    2、GCC是一个编译系统的驱动程序,负责解析输入的参数,依次调用预处理器(cpp)、编译器(ccl/cclplus)、汇编器(as)、链接器(ld)生成可执行文件。
    3、GCC 和 G++ 的区别并不是前者用来编译C代码,后者用来编译 C++ 代码。它们的区别是GCC把后缀为c的文件当C代码处理(ccl编译),而 G++ 则当作 C++ 处理(cclplus编译),对于后缀为cpp的文件gcc和 g++ 处理过程没有什么区别。GCC默认是不能编译 C++ 程序的,需要加上-lstdc++选项,G++ 是可以直接编译的,G++相当于是对GCC的封装。

    GCC编译过程

    在介绍GCC编译步骤之前,首先需要了解GCC支持的后缀文件类型。

    后缀名 对应语言
    .c C程序
    .C/.cc/.cxx C++程序
    .i 预处理后的C程序
    .ii 预处理后的C++程序
    .s/.S 汇编语言程序
    .h 头文件
    .o 目标文件
    .a/.so 编译后的库文件

    GCC编译流程分为四个步骤:预处理(生成.i/.ii文件)、编译(生成.s/.S文件)、汇编(生成.o文件)、链接(生成可执行文件)。


    gcc指令的一般格式为:
    gcc [选项] 要编译的文件 [选项] [目标文件]
    其中,目标文件可缺省,gcc默认生成可执行的文件为a.out


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

    1、预处理
    对于该阶段,gcc将stdio.h文件中的代码包含进这段程序,我们可以利用gcc -E test.c -o test.i命令来生成预处理过的.i文件。-E选项代表让gcc在预处理阶段后停止编译。test.i文件中的内容如下所示,可以看出stdio.h文件中的内容被展开。

    extern int fprintf (FILE *__restrict __stream,
      __const char *__restrict __format, ...);
      ......
      # 8 "test.c" 2
    int main()
    {
        printf("Hello World\n");
        return 0;
    }
    

    2、编译
    该阶段主要是对预编译后的.i文件编译,生成汇编代码的.s文件。gcc首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,gcc把代码翻译成汇编语言。我们可以使用-S选项来进行查看,该选项只进行编译而不进行汇编过程,生成汇编代码。
    我们可以利用gcc -S test.i -o test.s命令进行编译过程。test.s文件中的内容如下所示。

        .file   "test.c"
        .section    .rodata
    .LC0:
        .string "Hello World"
        .text
    .globl main
        .type   main, @function
    main:
        pushl   %ebp
        movl    %esp, %ebp
        andl    $-16, %esp
        subl    $16, %esp
        movl    $.LC0, (%esp)
        call    puts
        movl    $0, %eax
        leave
        ret
        .size   main, .-main
        .ident  "GCC: (GNU) 4.4.7 20120313 (Red Hat 4.4.7-16)"
        .section    .note.GNU-stack,"",@progbits
    

    3、汇编
    该阶段是将编译后的.s文件转化成二进制文件.o的过程,利用-c选项就可以生成二进制.o文件。我们可以利用gcc -c test.s -o test.o生成二进制代码。
    4、链接


    该阶段主要将成功编译的二进制文件进行链接操作,生成可执行文件。利用gcc test.o -o test生成可执行文件test,运行./test即可打印出hello world


    总结一下:

    gcc -E  test.c -o test.i    //把原代码交给cpp预处理器生成经过预处理后的中间文件test.i
    gcc -S test.i -o test.s     //把经过预处理之后的test.i文件交给编译器cc1生成test.s文件
    gcc -c test.s -o test.o     //把经过编译后汇编文件test.s交给as进行汇编,生成目标test.o文件
    gcc test.o -o test          //将as汇编之后的目标文件交给ld链接成一个可执行的文件test
    

    GCC常用编译选项

    1、总体编译选项

    选项名称 作用
    -c 只是编译不链接,生成目标文件.o
    -S 只是编译不汇编,生成汇编代码
    -E 只进行预编译,不做其他处理
    -g 在可执行程序中包含标准调试信息
    -o file 把输出文件输出到file里
    -v 打印出编译器内部编译各过程的命令行信息和编译器的版本
    -I dir 在头文件的搜索路径列表中添加dir目录
    -L dir 在库文件的搜索路径列表中添加dir目录
    -static 链接静态库
    -llibrary 连接名为library的库文件

    表中前边几个选项在GCC编译步骤中已经有所了解,主要对表中后四个选项进行介绍。
    当我们进行程序开发时,基本都会用到很多函数库,从程序员的角度看,函数库就是封装的一些头文件(.h)和库文件(.a/.so),Linux系统下头文件一般默认放到/usr/include/目录下,库文件放在/usr/lib/目录下,如果我们使用的库不在标准路径下,我们就需要指定头文件和库文件的路径以便让gcc找到它们。
    举个例子,假设上一节test.c主要实现C语言连接mysql数据库的功能,我们从官网下载的mysql connectors的头文件在/usr/local/mysq/include/路径下,库函数在/usr/local/mysql/lib/路径下。
    我们可以利用

    gcc -c -I /usr/local/mysql/include test.c -o test.o
    

    生成test.o二进制文件,再利用

    gcc -L /usr/local/mysql/lib -lmysqlclient test.o -o test
    

    进行链接操作产生可执行的test文件。这两步也可以合成一步,直接生成可执行的test文件

    gcc -I /usr/local/mysql/include -L /usr/local/mysql/lib -lmysqlclient test.c -o test
    

    Linux下的库文件分为两大类分别是动态链接库(通常以.so结尾)和静态链接库(通常以.a结尾),二者的区别仅在于程序执行时所需的代码是在运行时动态加载的,还是在编译时静态加载的。
    默认情况下, GCC在链接时优先使用动态链接库,只有当动态链接库不存在时才考虑使用静态链接库,如果需要使用静态链接库可以在编译时加上-static选项,强制使用静态链接库。
    比如在/usr/local/mysql/lib目录下有链接时所需要的库文件libmysqlclient.so和libmysqlclient.a,为了让GCC在链接时只用到静态链接库,可以使用下面的命令:
    gcc –L /usr/local/mysql/lib -static –lmysqlclient test.o –o test

    需要注意的是:

    • -I dir-L dir都只是指定了路径,而没有指定文件,因此不能在路径中包含文件名。
    • 另外值得详细解释一下的是-l选项, 它指示gcc去连接库文件libXXX.so。由于在Linux下的库文件命名时有一个规定:必须以lib三个字母开头。因此在用-l选项指定链接的库文件名时可以省去lib三个字母。也就是说gcc在对”-lXXX”进行处理时,会自动去链接名为libXXX.so的文件。

    2、其他常用编译选项

    选项名称 作用
    -ansi 支持符合ANSI标准的C程序
    -pedantic 允许发出ANSI C标准所列的全部警告信息
    -pedantic-error 允许发出ANSI C标准所列的全部错误信息
    -Wall 允许发出gcc提供的所有有用的报警信息
    -Werror 把所有的告警信息转化为错误信息,并在告警发生时终止编译过程
    -O 主要进行线程跳转(Thread Jump)和延迟退栈(Deferred Stack Pops)两种优化
    -O2 除了完成所有“-O1”级别的优化之外,同时还要进行一些额外的调整工作,如处理器指令调度等
    -O3 还包括循环展开和其他一些与处理器特性相关的优化工作
    -static 指示链接器构建一个完全链接的可执行程序,即链接静态库而不链接动态库
    -fPIC 指示链接器创建一个共享的目标文件,即so文件
    -shared 生成动态库,一般和上面的-fPIC一起使用

    参考文献:


    GCC学习总结

    Linux GCC常用命令

    GCC 编译详解

    相关文章

      网友评论

      • 思念那年慕云:你好,用gcc把三个.h文件和三个.c文件生成一个.a静态库,需要注意什么地方呀,因为我这边生成的.a在oc中报错,说找不到.h中暴露的函数。

      本文标题:GCC编译器总结

      本文链接:https://www.haomeiwen.com/subject/prazlttx.html