GCC总结

来源:互联网 发布:马刺 米尔斯 知乎 编辑:程序博客网 时间:2024/05/08 17:36

1.gcc所支持的后缀名解释

GNU CC(简称为Gcc)是GNU项目中符合ANSI C标准的编译系统,能够编译用C、C++和Object C等语言编写的程序。Gcc不仅功能强大,而且可以编译如C、C++、Object C、Java、Fortran、Pascal、Modula-3和Ada等多种语言,而且Gcc又是一个交叉平台编译器,它能够在当前CPU平台上为多种不同体系结构的硬件平台开发软件,因此尤其适合在嵌入式领域的开发编译。

         gcc -v  察看版本信息

                   下表是Gcc支持编译源文件的后缀及其解释。

                                  Gcc所支持后缀名解释

后 缀 名

所对应的语言

后 缀 名

所对应的语言

.c

C原始程序

.s/.S

汇编语言原始程序

.C/.cc/.cxx

C++原始程序

.h

预处理文件(头文件)

.m

Objective-C原始程序

.o

目标文件

.i

已经过预处理的C原始程序

.a/.so

编译后的库文件

.ii

已经过预处理的C++原始程序

  

Gcc的编译流程分为了四个步骤,分别为:

· 预处理(Pre-Processing)

· 编译(Compiling)

· 汇编(Assembling)

· 链接(Linking)


2.Gcc编译的流程

文本编辑工具(VI/Emac)等----------->>>>>C源代码(.c)----------->>>>>编译器(gcc)----------->>>>>汇编代码(.S)----------->>>>>汇编器(as)----------->>>>>目标代码----------->>>>>+其他目标代码----------->>>>>连接器(ld)----------->>>>>可执行程序


目标代码到可执行程序之间还有一些辅助工具:

ar.nm     strings    objcopy    strip      readelf


可用gdb进行调试


3.GCC指令的一般格式

     gcc [选项]要编译的文件 [选项] [目标文件]

4.GCC的工作过程

  hello.c

#include <stdio.h>int main(){printf("Hello world!");return 0;}
     预处理阶段(将头文件、预定义等东西进行替换)

    在该阶段,编译器将上述代码中的stdio.h编译进来,并且用户可以使用Gcc的选项”-E”进行查看,该选项的作用是让Gcc在预处理结束后停止编译过程。

gcc -E hello.c -o hello.i

在此处,选项”-o”是指目标文件”.i”文件为已经过预处理的C原始程序。以下列出了hello.i文件的部分内容:

//hello.i的部分代码# 1 "hello.c"# 1 "<built-in>"# 1 "<命令行>"# 1 "hello.c"# 1 "/usr/include/stdio.h" 1 3 4# 28 "/usr/include/stdio.h" 3 4# 1 "/usr/include/features.h" 1 3 4# 324 "/usr/include/features.h" 3 4# 1 "/usr/include/i386-linux-gnu/bits/predefs.h" 1 3 4# 325 "/usr/include/features.h" 2 3 4# 357 "/usr/include/features.h" 3 4# 1 "/usr/include/i386-linux-gnu/sys/cdefs.h" 1 3 4# 378 "/usr/include/i386-linux-gnu/sys/cdefs.h" 3 4# 1 "/usr/include/i386-linux-gnu/bits/wordsize.h" 1 3 4# 379 "/usr/include/i386-linux-gnu/sys/cdefs.h" 2 3 4# 358 "/usr/include/features.h" 2 3 4# 389 "/usr/include/features.h" 3 4# 1 "/usr/include/i386-linux-gnu/gnu/stubs.h" 1 3 4# 1 "/usr/include/i386-linux-gnu/bits/wordsize.h" 1 3 4# 5 "/usr/include/i386-linux-gnu/gnu/stubs.h" 2 3 4
   编译阶段(检查代码的规范性,语法错误,无误后翻译成汇编语言)

    接下来进行的是编译阶段,在这个阶段中,Gcc首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,Gcc把代码翻译成汇编语言。用户可以使用”-S”选项来进行查看,该选项只进行编译而不进行汇编,生成汇编代码。

  gcc -S hello.i -o hello.s


//hello.s部分代码        .file   "hello.c"        .section        .rodata.LC0:        .string "Hello world!"        .text        .globl  main        .type   main, @functionmain:.LFB0:        .cfi_startproc        pushl   %ebp        .cfi_def_cfa_offset 8        .cfi_offset 5, -8        movl    %esp, %ebp        .cfi_def_cfa_register 5        andl    $-16, %esp        subl    $16, %esp        movl    $.LC0, %eax        movl    %eax, (%esp)        call    printf        movl    $0, %eax        leave        .cfi_restore 5

        汇编阶段(将汇编文件.s转换成目标文件---二进制目标代码)

汇编阶段是把编译阶段生成的”.s”文件转成目标文件,读者在此可使用选项”-c”就可看到汇编代码已转化为”.o”的二进制目标代码了。

   gcc -c hello.s -o hello.o

        链接阶段(将多个目标文件链接生成可知执行文件)

  gcc hello.o -o hello

在成功编译之后,就进入了链接阶段。在这里涉及到一个重要的概念:函数库。

读者可以重新查看这个小程序,在这个程序中并没有定义”printf”的函数实现,且在预编译中包含进的”stdio.h”中也只有该函数的声明,而没有定义函数的实现,那么,是在哪里实现”printf”函数的呢?最后的答案是:系统把这些函数实现都被做到名为libc.so.6的库文件中去了,在没有特别指定时,Gcc会到系统默认的搜索路径”/usr/lib”下进行查找,也就是链接到libc.so.6库函数中去,这样就能实现函数”printf”了,而这也就是链接的作用。

函数库一般分为静态库和动态库两种。静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了。其后缀名一般为”.a”。动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以节省系统的开销。动态库一般后缀名为”.so”,如前面所述的libc.so.6就是动态库。Gcc在编译时默认使用动态库。

完成了链接之后,Gcc就可以生成可执行文件,如下所示。


 

指令助记方法(各选项以此为ESc 生成文件后缀为iso)


4.gcc编译选项

Gcc总体选项列表

后缀名

所对应的语言

-c

只是编译不链接,生成目标文件“.o”

-S

只是编译不汇编,生成汇编代码

-E

只进行预编译,不做其他处理

-g

在可执行程序中包含标准调试信息

-o file

把输出文件输出到file里

-v

打印出编译器内部编译各过程的命令行信息和编译器的版本

-I dir

在头文件的搜索路径列表中添加dir目录(/usr/include)

-L dir

在库文件的搜索路径列表中添加dir目录(/usr/lib)

-static

链接静态库

-llibrary

连接名为library的库文件

 -O                                    进行优化

-O2                                   进行优化比-O的优化程度还好(通过time ./可执行程序计算要需要的时间来进行比较)

对于“-c”、“-E”、“-o”、“-S”选项在前已经讲解了其使用方法,在此主要讲解另外两个非常常用的库依赖选项“-I dir”和“-L dir”。

      库依赖  

  c语言开发完全不是用第三方的函数库的情况是比较少的,从程序员的角度看,库函数就是一些头文件(.h)和库文件(.so或.a)。默认情况下,头文件在/usr/include 库文件在/usr/lib目录下。

· “-I dir”

正如上表中所述,“-I dir”选项可以在头文件的搜索路径列表中添加dir目录。GCC使用用户指定的程序库.由于Linux中头文件都默认放到了“/usr/include/”目录下,因此,当用户希望添加放置在其他位置的头文件时,就可以通过“-I dir”选项来指定,这样,Gcc就会到相应的位置查找对应的目录。

比如在“/root/workplace/Gcc”下有两个文件:

 

 

#include<my.h>

int main()

{

printf(“Hello!!n”);

return 0;

}

 

#include<stdio.h>

 

这样,就可在Gcc命令行中加入“-I”选项:

 

[root@localhost Gcc] Gcc hello1.c –I /root/workplace/Gcc/ -o hello1

 

这样,Gcc就能够执行出正确结果。

 

小知识

在include语句中,“<>”表示在标准路径中搜索头文件,““””表示在本目录中搜索。故在上例中,可把hello1.c的“#include<my.h>”改为“#include “my.h””,就不需要加上“-I”选项了。


 

· “-L dir”

选项“-L dir”的功能与“-I dir”类似,用于告诉GCC在<dir_name>中去寻找函数库,能够在库文件的搜索路径列表中添加dir目录。例如有程序hello_sq.c需要用到目录“/root/workplace/Gcc/lib”下的一个动态库libsunq.so,则只需键入如下命令即可:

 

[root@localhost Gcc] Gcc hello_sq.c –L /root/workplace/Gcc/lib –lsunq –o hello_sq

 

-llibrary

连接名为library的库文件

需要注意的是,“-I dir”和“-L dir”都只是指定了路径,而没有指定文件,因此不能在路径中包含文件名。

另外值得详细解释一下的是“-L”选项,它指示Gcc去连接库文件libsunq.so。由于在Linux下的库文件命名时有一个规定:必须以lib三个字母开头。因此在用-l选项指定链接的库文件名时可以省去lib三个字母。也就是说Gcc在对”-lsunq”进行处理时,会自动去链接名为libsunq.so的文件。(重复解释)在Linux中,函数库的命名是遵循UNIX约定的,即lib{lib name},例如libsocket.so,所以当你需要告诉GCC使用这些库的时候,你就可以使用-lsocket选项。


GCC在默认情况下使用共享库来链接程序,而当你想链接静态库的时候,一定要使用-static选项

5.告警和出错选项

                                    Gcc总体选项列表

选项

含义

-ansi

支持符合ANSI标准的C程序

-pedantic

允许发出ANSI C标准所列的全部警告信息

选项

含义

-pedantic-error

允许发出ANSI C标准所列的全部错误信息

-w

关闭所有告警

-Wall

允许发出Gcc提供的所有有用的报警信息

-werror

把所有的告警信息转化为错误信息,并在告警发生时终止编译过程

 

下面结合实例对这几个告警和出错选项进行简单的讲解。

如有以下程序段:

 

//warning.c#include<stdio.h>void main(){long long tmp = 1;printf(“This is a bad code!\n”);return 0;}


 

这是一个很糟糕的程序,读者可以考虑一下有哪些问题?

· “-ansi”

该选项强制Gcc生成标准语法所要求的告警信息,尽管这还并不能保证所有没有警告的程序都是符合ANSI C标准的。运行结果如下所示:

 

 Gcc –ansi warning.c –o warning

warning.c: 在函数“main”中:

warning.c:7 警告:在无返回值的函数中,“return”带返回值

warning.c:4 警告:“main”的返回类型不是“int”

 

可以看出,该选项并没有发现”long long”这个无效数据类型的错误。

· “-pedantic”

允许发出ANSI C标准所列的全部警告信息,同样也保证所有没有警告的程序都是符合ANSI C标准的。其运行结果如下所示:

 

[root@localhost Gcc]# Gcc –pedantic warning.c –o warning

warning.c: 在函数“main”中:

warning.c:5 警告:ISO C90不支持“long long”

warning.c:7 警告:在无返回值的函数中,“return”带返回值

warning.c:4 警告:“main”的返回类型不是“int”

 

可以看出,使用该选项查看出了”long long”这个无效数据类型的错误。

· “-Wall”

允许发出Gcc能够提供的所有有用的报警信息。该选项的运行结果如下所示:

[root@localhost Gcc]# Gcc –Wall warning.c –o warning

warning.c:4 警告:“main”的返回类型不是“int”

warning.c: 在函数”main”中:

warning.c:7 警告:在无返回值的函数中,”return”带返回值

warning.c:5 警告:未使用的变量“tmp”

 

使用“-Wall”选项找出了未使用的变量tmp,但它并没有找出无效数据类型的错误。

另外,Gcc还可以利用选项对单独的常见错误分别指定警告,有关具体选项的含义感兴趣的读者可以查看Gcc手册进行学习。

6.优化选项

Gcc可以对代码进行优化,它通过编译选项“-On”来控制优化代码的生成,其中n是一个代表优化级别的整数。对于不同版本的Gcc来讲,n的取值范围及其对应的优化效果可能并不完全相同,比较典型的范围是从0变化到2或3。

不同的优化级别对应不同的优化处理工作。如使用优化选项“-O”主要进行线程跳转(Thread Jump)和延迟退栈(Deferred Stack Pops)两种优化。使用优化选项“-O2”除了完成所有“-O1”级别的优化之外,同时还要进行一些额外的调整工作,如处理器指令调度等。选项“-O3”则还包括循环展开和其他一些与处理器特性相关的优化工作。

虽然优化选项可以加速代码的运行速度,但对于调试而言将是一个很大的挑战。因为代码在经过优化之后,原先在源程序中声明和使用的变量很可能不再使用,控制流也可能会突然跳转到意外的地方,循环语句也有可能因为循环展开而变得到处都有,所有这些对调试来讲都将是一场噩梦。所以笔者建议在调试的时候最好不使用任何优化选项,只有当程序在最终发行的时候才考虑对其进行优化。

7.体系结构相关选项


                                           Gcc体系结构相关选项列表

选项

含义

-mcpu=type

针对不同的CPU使用相应的CPU指令。可选择的type有i386、i486、pentium及i686等

-mieee-fp

使用IEEE标准进行浮点数的比较

-mno-ieee-fp

不使用IEEE标准进行浮点数的比较

-msoft-float

输出包含浮点库调用的目标代码

-mshort

把int类型作为16位处理,相当于short int

-mrtd

强行将函数参数个数固定的函数用ret NUM返回,节省调用函数的一条指令

 

这些体系结构相关选项在嵌入式的设计中会有较多的应用


原创粉丝点击