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

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

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

hello.c:

  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的过程。

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

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

  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格式。

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

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

  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

三、汇编

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

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

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

四、链接

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

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

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

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

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

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

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

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

  1. gcc -sharedtest.so test.o

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

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

GCC编译选项

1. -c

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

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

2. -o

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

  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.如对下面这代码:

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

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

5. -I

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

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

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

6. -E

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

  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编译程序,专门用于显示警告用!!

  1. gcc -Wall hello.c

9. -L

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

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

10.-share   

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

11.-static  

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

12.-fPIC

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

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

  1. Linux学习---GCC编译过程

    (一)GCC编译过程 预处理 cpp -o a.i a.c     //生成预处理文件 等同于[gcc -E] //预处理为将宏定义(#define)等进行替换. 编译 /user/lib/gcc/i ...

  2. GCC编译过程与动态链接库和静态链接库

    1. 库的介绍 库是写好的现有的,成熟的,可以复用的代码.现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常. 本质上来说库是一种可执行代码的二进制形式,可 ...

  3. 1.GCC编译过程

    一. GCC编译过程 gcc -E hello.c -o hello.i // 预处理.将代码中包含的头文件和宏进行替换 gcc -S hello.i -o hello.s // 汇编.将当前文本转换 ...

  4. unix gcc编译过程

    gcc编译过程 现代编译器常见的编译过程: 源文件-->预处理-->编译/优化-->汇编-->链接-->可执行文件 对于gcc而言: 第一步 预处理       命令: ...

  5. gcc 编译过程

    gcc 编译过程从 hello.c 到 hello(或 a.out)文件, 必须历经 hello.i. hello.s. hello.o,最后才得到 hello(或a.out)文件,分别对应着预处理. ...

  6. GCC编译过程

    以下是C程序一般的编译过程: gcc的编译流程分为四个步骤,分别为:· 预处理(Pre-Processing) 对C语言进行预处理,生成*.i文件.· 编译(Compiling) 将上一步生成的*.i ...

  7. Linux系统GCC常用命令和GCC编译过程描述

    前言: GCC 原名为 GNU C 语言编译器(GNU C Compiler),因为它原本只能处理 C语言.GCC 很快地扩展,变得可处理 C++.后来又 扩展能够支持更多编程语言,如Fortran. ...

  8. gcc编译过程简述

    在linux系统上,从源文件到目标文件的转化是由编译器完成的.以hello.c程序的编译为例,如下: dfcao@linux: gcc -o hello hello.c 在这里,gcc编译器读取源文件 ...

  9. C语言简短程序gcc编译过程

    一.建立一个×.c源文件.这里起名:rocks.c 二.编辑源代码,在c源文件内输入如下代码: #include <stdio.h> int main() { puts("C R ...

随机推荐

  1. Windows Server 2008 R2 可能会碰到任务计划无法自动运行的解决办法

    在做Windows Server 2008R2系统的计划任务时使用到了bat脚本,手动启动没问题,自动执行缺失败,代码:0x2. 将“操作”的“起始于”进行设置了bat脚本的目录即可.

  2. 【UE4】二十四、UE4内部版本引擎和官方版本引擎版本保持兼容的方法

    内部使用的引擎和官方正式发布的引擎版本号不一致,这种情况会导致一些插件由于版本不一致无法使用,有其是在没有插件源码的情况下.解决方法为 修改Engine\Source\Runtime\Launch\R ...

  3. 初见spark-01

    今天我们来学习spark,spark是一种快速,通用,可扩展的大数据分析引擎,现已成为Apache顶级项目,Spark是MapReduce的替代方案,而且兼容HDFS,Hive,可融入Hadoop的生 ...

  4. PHP.19-验证码生成

    生成验证码 思路:先定义验证码函数getCode() //绘制验证码 $num = 4; //字符长度 getCode($num, 2); 1.创建画布,分配颜色 imagecreatetruecol ...

  5. 11 Django组件-分页器

    Django的分页器(paginator) view from django.shortcuts import render,HttpResponse # Create your views here ...

  6. tp事务处理

    数据库事务处理见手册 逻辑事务处理 Model->startTrans(); // 开启事务 if(操作失败) { Model->rollback(); // 回滚 }else { Mod ...

  7. Lazarus教程 中文版后续给出

    市面上有介绍Delphi的书籍(近来Delphi的书也是越来越少了),但没有一本系统的介绍Lazarus的书,这本书是网上的仅有的一本Lazarus教程,目前全部是英文,不过我已经着手开始翻译,争取尽 ...

  8. USACO Section2.3 Zero Sum 解题报告 【icedream61】

    zerosum解题报告----------------------------------------------------------------------------------------- ...

  9. ajax向Asp.NET后端传递数组型数据

    近日,在开发一个组件的过程中,需要通过Ajax对象向Asp.NET后端传递一个比较复杂的表单,表单中的一个字段是数组类型,我能想到的办法是用JSON.stringify将前端的数组对象序列化成字符串, ...

  10. appium-手势密码实现-automationName 是Appium的情况

    1. 红色区域的范围为:[66,575][1014,1523], 由于这块是一个整块,所以无法使用每个点的数据:因此只能使用LockPatternView对象拿到左上角的坐标值 2.  原理, 将九宫 ...