和菜鸟一起学c之gcc编译过程及其常用编译选项【转】

转自:http://blog.csdn.net/eastmoon502136/article/details/8162626

版权声明:本文为博主东月之神原创文章,未经博主允许不得转载。

       上篇文章,知道了,C代码编译后存放在内存中的位置,那么C代码的整个编译过程又是怎样的呢?一条命令gcc hello.c就可以编译成可执行程序a.out,然后./a.out之后就可以执行hello.c这个程序的代码了。下面的文章分析的不错,就整理了下。

hello.c:

 

 

[html] view plain copy

  1. #include<stdio.h>  
  2. int main()  
  3. {  
  4.         printf(“Hello World\n”);  
  5.         return 0;  
  6. }  

 

实际上gcc hello.c可以分解为4个步骤,分别是预处理(Preprocess),编译(Compilation),汇编(Assembly)和链接(Linking)。

 

一、预处理

预处理过程主要读取c源程序,对伪指令和特殊符号进行处理。包括宏,条件编译,包含的头文件,以及一些特殊符号。基本上是一个replace的过程。

 

[html] view plain copy

  1. gcc –E hello.c –o hello.i  

以下为预处理后的输出文件hello.i的内容

 

[html] view plain copy

  1. # 1"hello.c"  
  2. # 1"<built-in>"  
  3. # 1"<command-line>"  
  4. # 1"hello.c"  
  5. # 1 "/usr/include/stdio.h"1 3 4  
  6. # 28"/usr/include/stdio.h" 3 4  
  7. /***** 省略了部分内容,包括stdio.h中的一些声明及定义  *****/  
  8. # 2"hello.c" 2  
  9. int main()  
  10. {  
  11.  printf("Hello World\n");  
  12.  return 0;  
  13. }  

 

 

预处理过程主要处理规则如下:

1、将所有的#define删除,并且展开所有的宏定义;

2、处理所有条件编译指令,如#if,#ifdef等;

3、处理#include预编译指令,将被包含的文件插入到该预编译指令的位置。该过程递归进行,及被包含的文件可能还包含其他文件。

4、删除所有的注释//和 /**/;

5、添加行号和文件标识,如#2 “hello.c” 2,以便于编译时编译器产生调试用的行号信息及用于编译时产生编译错误或警告时能够显示行号信息;

6、保留所有的#pragma编译器指令,因为编译器须要使用它们;

 

二、编译

编译过程通过词法和语法分析,确认所有指令符合语法规则(否则报编译错),之后翻译成对应的中间码,在Linux中被称为RTL(Register
Transfer
Language),通常是平台无关的,这个过程也被称为编译前端。编译后端对RTL树进行裁减,优化,得到在目标机上可执行的汇编代码。gcc采用as作为其汇编器,所以汇编码是AT&T格式的,而不是Intel格式,所以在用gcc编译嵌入式汇编时,也要采用AT&T格式。

 

[html] view plain copy

  1. gcc –S hello.i –o hello.s  

以下为编译后的输出文件hello.s的内容

[html] view plain copy

  1.     .file  "hello.c"  
  2.         .section    .rodata  
  3. .LC0:  
  4.         .string      "HelloWorld"  
  5.         .text  
  6. .globl main  
  7.         .type         main, @function  
  8. main:  
  9.         pushl         %ebp  
  10.         movl          %esp, %ebp  
  11.         andl $-16, %esp  
  12.         subl  $16, %esp  
  13.         movl          $.LC0, (%esp)  
  14.         call   puts  
  15.         movl          $0, %eax  
  16.         leave  
  17.         ret  
  18.         .size main, .-main  
  19.         .ident        "GCC: (GNU)4.4.0 20090506 (Red Hat 4.4.0-4)"  
  20.         .section   .note.GNU-stack,"",@progbits  

 

三、汇编

汇编器是将汇编代码转变成机器可以执行的命令,每一个汇编语句几乎都对应一条机器指令。汇编相对于编译过程比较简单,根据汇编指令和机器指令的对照表一一翻译即可。

       

[html] view plain copy

  1. gcc –c hello.c –o hello.o  

 

由于hello.o的内容为机器码,不能以文本形式方便的呈现。

 

四、链接

链接器ld将各个目标文件组装在一起,解决符号依赖,库依赖关系,并生成可执行文件。

 

[html] view plain copy

  1. ld –static crt1.o crti.o crtbeginT.ohello.o –start-group –lgcc –lgcc_eh –lc-end-group crtend.o crtn.o  

(省略了文件的路径名)。

 

当然链接的时候还会用到静态链接库,和动态连接库。静态库和动态库都是.o目标文件的集合。

静态库是在链接过程中将相关代码提取出来加入可执行文件的库(即在链接的时候将函数的代码将从其所在地静态链接库中被拷贝到最终的可执行程序中),ar只是将一些别的文件集合到一个文件中。可以打包,当然也可以解包。

 

[html] view plain copy

  1. ar -v -q  test.a test.o  

上面指令可以生成静态链接库test.a

 

动态库在链接时只创建一些符号表,而在运行的时候才将有关库的代码装入内存,映射到运行时相应进程的虚地址空间。如果出错,如找不到对应的.so文件,会在执行的时候报动态连接错(可用LD_LIBRARY_PATH指定路径)。用file
test.so可以看到test.so是shared object的ELF文件。

 

[html] view plain copy

  1. gcc -sharedtest.so test.o  

 

上面指令可以生成动态连接库test.so

 

好了,整个编译过程就如上所示了,那么对于gcc还有一些编译的选项的。具体如下:

 

GCC编译选项

1. -c    

           编译产生对象文件(*.obj)而不链接成可执行文件,当编译几个独立的模块,而待以后由链接程序把它们链接在一起时,就可以使用这个选项,如:

           

[html] view plain copy

  1. gcc -c hello.c ===> hello.o  
  2. gcc hello.o  

 

 

 

2. -o    

         允许用户指定输出文件名,如

 

[html] view plain copy

  1. gcc hello.c -o hello.o  
  2. or  
  3. gcc hello.c -o hello  

 

 

3. -g   

         指明编译程序在编译的输出中应产生调试信息.这个调试信息使源代码和变量名引用在调试程序中或者当程序异常退出后在分析core文件时可被使用.

 

4. -D  

       允许从编译程序命令行定义宏符号

    一共有两种情况:一种是用-DMACRO,相当于在程序中使用#define MACRO,另一种是用-DMACRO=A,相当于程序中的#define MACRO A.如对下面这代码:

 

[html] view plain copy

  1. #ifdef DEBUG  
  2.      printf("debugmessage\n");  
  3. #endif  

 

 

     编译时可加上-DDEBUG参数,执行程序则打印出编译信息

 

5. -I  

         可指定查找include文件的其他位置.例如,如果有些include文件位于比较特殊的地方,比如/usr/local/include,就可以增加此选项如下:

 

[html] view plain copy

  1. gcc -c -I/usr/local/include -I/opt/include hello.c  

 

        此时目录搜索会按给出的次序进行.

 

6. -E  

         这个选项是相对标准的,它允许修改命令行以使编译程序把预先处理的C文件发到标准输出,而不实际编译代码.在查看C预处理伪指令和C宏时,这是很有用的.可能的编译输出可重新定向到一个文件,然后用编辑程序来分析:

 

[html] view plain copy

  1. gcc -c -E hello.c >cpp.out     

   

     此命令使include文件和程序被预先处理并重定向到文件cpp.out.以后可以用编辑程或者分页命令分析这个文件,并确定最终的C语言代码看起来如何.

 

7. -O  

       优化选项,这个选项不是标准的

           -O和 -O1指定1级优化

           -O2 指定2级优化

           -O3 指定3级优化

           -O0指定不优化

           gcc -c O3 -O0 hello.c 

       当出现多个优化时,以最后一个为准!!

 

8. -Wall 

       以最高级别使用GNU编译程序,专门用于显示警告用!!

 

[html] view plain copy

  1. gcc -Wall hello.c  

 

9. -L

       指定连接库的搜索目录,-l(小写L)指定连接库的名字

 

[html] view plain copy

  1. gcc main.o -L/usr/lib -lqt -o hello  

[html] view plain copy

  1.   

10.-share   

       此选项将尽量使用动态库,所以生成文件比较小,但是需要系统由动态库

 

11.-static  

        此选项将禁止使用动态库,所以,编译出来的东西,一般都很大,也不需要什么动态连接库

 

12.-fPIC

        表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动态载入时是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。

时间: 2024-09-29 22:39:46

和菜鸟一起学c之gcc编译过程及其常用编译选项【转】的相关文章

Linux gcc编译简介、常用命令

gcc作为linux平台下的标准C编译器,功能很强大.格式如下: gcc [option] [filename] -x language 指定使用的语言 -c 对文件进行编译和汇编,但不进行连接 -S 对文件进行编译,但不汇编和连接 -E 仅对文件进行预处理 -o 编译成可执行文件 -wall 显示附加的警告信息 -g 显示错误信息以便调试(调试标记) -O1/O2/O3 对代码进行优化,数字越大优化等级越高 -v 查看gcc版本 使用gcc高级选项 下面命令,gcc将编译每一个c程序,并把他们

OS X下gcc编译报错,Xcode带的gcc

问题描述 OS X下gcc编译报错,Xcode带的gcc 错误是 dpc2sim.a file was built for archive which is not the architecture being linked (x86_64) 用Data Prefetch Championship给的framework写prefetch的程序http://comparch-conf.gatech.edu/dpc2/想先跑跑人家给的example,可是编译的时候就报错了.我用的是OS X,gcc版

菜鸟刚学html5,要通过什么方法才能在网页上显示表格,表格内容来自远程数据库sql

问题描述 菜鸟刚学html5,要通过什么方法才能在网页上显示表格,表格内容来自远程数据库sql 要在网页上显示表格内容,内容来自远程数据库sql,网页是asp.net 解决方案 asp.net不是有datalist,repater那种控件,你用repeater 的itemtemplate模板放tr,然后设置repeater数据源进行绑定就行了 解决方案二: 应该可以在前台用ajax获取 我个人之前试用过的方法是让后台生成接口,前台直接调用接口获取数据,并呈现出来. 解决方案三: 和html5没有

gcc vc++6.0-同样代码VC6.0结果正确,GCC编译运行结果不正确,原因是什么呢?谢谢!

问题描述 同样代码VC6.0结果正确,GCC编译运行结果不正确,原因是什么呢?谢谢! #include int main() { char deno[1000]={''},num,i=0,remain=0; scanf("%s %d",deno,&num); //获取被除数.除数 if(!num) return (-1); while(deno[i]!='') deno[i++]-='0'; //转换成数字 deno[i]='#'; //结束标志 i=0; while(deno

ubuntu_14.04下gcc编译问题

问题描述 ubuntu_14.04下gcc编译问题 main函数体仅打印hello,world,gcc发现如下问题,不解: 头文件无#include,gcc不报错而报警告,为什么? 函数体中使用"exit(EXIT_SUCCESS);"代替"return(0);"需要使用头文件#include .如果头文件不加入,会报错 : "hello.c:9:7: error: 'EXIT_SUCCESS' undeclared (first use in this

c语言-Linux下使用gcc编译c11程序是不是过于麻烦了?

问题描述 Linux下使用gcc编译c11程序是不是过于麻烦了? 每次都要输入-std=c11 是不是过于麻烦了?怎么让它默认为-std=c11? 解决方案 因为它默认是c89等标准,所以需要指定 你可以写一个makefile ,这样每次只用make来编译

java开发问题(本人菜鸟,学JAVA半年)

问题描述 java开发问题(本人菜鸟,学JAVA半年) 问题很弱,就是如何开发出JAVA的软件. 因为学习JAVA一段时间了,对基础知识也有一定的掌握.但编写的小程序一直都只是在开发软件的JVM虚拟机上运行,我想知道怎么样才能让我的程序离开JVM. 就像<植物大战僵尸>,我在开发软件里有写出一个可以运行的,但如果要给其他人用的话我就不知道怎么做了. 求指教.感谢 啊! 解决方案 你必须自己打好一个jar包,和exe是一样的!点击就会运行!前提是用户必须安装jdk中的jre或者你把jre打进包中

gcc编译出现了undefined reference to `exp&amp;amp;#39;

问题描述 gcc编译出现了undefined reference to `exp' 在Ubuntu下编译word.cgcc -lm -pthread word.c -o word还是出现了undefined reference to `exp',后来有gcc -pthread word.c -o word /usr/lib/libm.a则提示没有/usr/lib/libm.a这个文件, 有没有有经验的大侠有解决办法? 解决方案 首先确定有libm.so,虽然一般是肯定有的,locate libm

在ubuntu使用gcc编译一个基本签名算法出现 段错误 核心已转储 麻烦给看看程序有什么错误。

问题描述 在ubuntu使用gcc编译一个基本签名算法出现 段错误 核心已转储 麻烦给看看程序有什么错误. 可能程序存在不止一个错误,如果可以,麻烦一一指出 如果哪位高手能帮忙让程序跑出结果,我把剩下的140C送给他作为感谢! #include "pbc.h" int main(void) { pairing_t pairing; element_t g, h; element_t public_key, sig; element_t secret_key; element_t tem