在linux系统上,从源文件到目标文件的转化是由编译器完成的。以hello.c程序的编译为例,如下:

dfcao@linux: gcc -o hello hello.c

在这里,gcc编译器读取源文件hello.c,并把它翻译成一个可执行文件 hello。

这个翻译过程可分为四个阶段逐步完成:预处理,编译,汇编,链接,如下图所示。

逐步做下简单分析:

在未编译前,hello.c 的源代码如下

#include <stdio.h>

int main()
{
printf("hello, world\n");
}

第一步、预处理阶段

执行命令: gcc -o hello.i -E hello.c

    或者 cpp -o hello.i hello.c (这里cpp不值c plus plus,而是预处理器the C Preprocessor)

预处理器cpp根据以字符开头#开头的命令,修改原始C程序。比如hello.c中的第一行为 #include <stdio.h>,预处理器便将stdio.h的内容直接插入到程序中。

预处理之后得到文本文件hello.i,打开如下

#  "hello.c"
# "<built-in>"
# "<命令行>"
# "hello.c"
# "/usr/include/stdio.h"
...
...
...
extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));
# "/usr/include/stdio.h" # "hello.c" int main()
{
printf("hello, world\n");
}

在源代码的前面插入了stdio.h,整个hello.i 的行数由hello.c的6行变到了855行!

第二步、编译阶段

执行命令: gcc -o hello.s -S hello.i

    或者 ccl -o hello.s hello.i

编译器ccl 将文本文件hello.i 翻译为hello.s,这个文件里面包含一个汇编程序,如下

    .file    "hello.c"
.section .rodata
.LC0:
.string "hello, world"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset
.cfi_offset , -
movl %esp, %ebp
.cfi_def_cfa_register
andl $-, %esp
subl $, %esp
movl $.LC0, (%esp)
call puts
leave
.cfi_restore
.cfi_def_cfa ,
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"
.section .note.GNU-stack,"",@progbits

汇编语言是非常有用的,因为它将不同高级语言的不同编译器提供了通用的输出语言。例如,C和Fortran 的在此步编译产生的输出文件都是一样的汇编语言。

第三步、汇编阶段

执行命令: gcc -o hello.o -c hello.s

    或者 as -o hello.o hello.s

汇编器as 将hello.s 翻译成机器语言保存在hello.o 中。这是个二进制文件,用命令hexdump hello.o 打开如下

 457f 464c      

 011c
000d 000a 83e5 f0e4 ec83 c710
fce8 ffff c9ff 00c3 6c6c
2c6f 726f 646c 3a43
6e75 4c2f 6e69 206f 2e34
2e36 2d33 746e 2e34
2e36 7a01
7c01 0c1b 001c
00000a0 001c 080e
00000b0 0d42 0cc5 2e00
00000c0 746d 2e00 2e00
00000d0 2e00 2e6c
00000e0 2e00 2e00 722e 646f
00000f0 632e 6d6f 656d 746e 2e00 6f6e
472e 554e 732d 6b63 2e00
2e6c 665f 656d *
001f 001b
03e8
000b 00001a0 004c

第四步、链接阶段

执行命令: gcc -o hello hello.o

    或者 ld -o hello hello.o

注意!hello程序调用了printf 函数,这个函数是标准C库中的一个函数,他保存在一个名为printf.o 的文件中,这个文件必须以某种方式合并到我们的hello.o的程序中。

链接器ld 负责处理这种合并。结果得到hello 可执行文件,可以被加载到内存中由系统执行。

./hello

总结

编译器的编译过程:
源文件-->预处理-->编译/优化-->汇编-->链接-->可执行文件。

平常用的最多、看起来一句命令就搞定的一次编译背后其实非常不简单。此博作简单记录,能力到了之后再做深入分析。

#---------------------------------------------------------------------------------#

参考文献

《Computer Systems: A Programmer's Perspective》 by Randal Bryant.

“编译过程” by itwilliamleehttp://www.lisdn.com/html/95/n-15195.html

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. system 系统调用、gcc编译过程

    system 库函数的功能是执行操作系统的命令或者运行指定的程序 #include <stdio.h> #include <stdlib.h>//引入库 int main() ...

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

    转自:http://blog.csdn.net/eastmoon502136/article/details/8162626 版权声明:本文为博主东月之神原创文章,未经博主允许不得转载. 上篇文章,知 ...

随机推荐

  1. iOS-LaunchImage启动页

    一. 目标: 设置一个漂亮的启动页. 二.步骤 1. 先创建LaunchImage 2. 进一步设置需要适配的启动页机型 3. 设置完成的效果 4.往里面拖图片,如图是适配的图片的配置 5. 在项目中 ...

  2. css常用的特效代码

    一.网页变灰的代码:a) 网页变灰色<head>加到这里</head><style type="text/css">html {FILTER: ...

  3. 【JVM】JVM系列之内存模型(六)

    一.前言 经过前面的学习,我们终于进入了虚拟机最后一部分的学习,内存模型.理解内存模型对我们理解虚拟机.正确使用多线程编程提供很大帮助.下面开始正式学习. 二.Java并发基础 在并发编程中存在两个关 ...

  4. 【知识积累】SBT+Scala+MySQL的Demo

    一.背景 由于项目需要,需要在Sbt+Scala项目中连接MySQL数据库.由于之前使用Maven+Java进行依赖管理偏多,在Sbt+Scala方面也在不断进行摸索,特此记录,作为小模块知识的积累. ...

  5. 【C#】DataRowState演变备忘

    环境:.net 2.0 DataRow的行状态一段时间不用就会吃不准,记录一下,备查. DataRowState 演变表 行属于如下状态时进行右边操作→ 后的状态演变 添加到表 dt.Rows.Add ...

  6. PHP+MYSQL网站SQL Injection攻防

    程序员们写代码的时候讲究TDD(测试驱动开发):在实现一个功能前,会先写一个测试用例,然后再编写代码使之运行通过.其实当黑客SQL Injection时,同样是一个TDD的过程:他们会先尝试着让程序报 ...

  7. Overload 和Override 的区别

    Overload 是重载的意思,Override 是覆盖的意思,也就是重写.重载 Overload 表示同一个类中可以有多个名称相同的方法,但这些方法的参数列表各不相同(即参数个数或类型不同).重写 ...

  8. Oracle学习总结_day03_day04_条件查询_排序_函数_子查询

    本文为博主辛苦总结,希望自己以后返回来看的时候理解更深刻,也希望可以起到帮助初学者的作用. 转载请注明 出自 : luogg的博客园 谢谢配合! day03_条件查询_排序_函数 清空回收站: PUR ...

  9. Direct3D设备管理器(Direct3D device manager)

    这几天在做dxva2硬件加速,找不到什么资料,翻译了一下微软的两篇相关文档.并准备记录一下用ffmpeg实现dxva2,将在第三篇写到.这是第一篇,英文原址:https://msdn.microsof ...

  10. 从零开始学Python第八周:网络编程基础(socket)

    Socket网络编程 一,Socket编程 (1)Socket方法介绍 Socket是网络编程的一个抽象概念.通常我们用一个Socket表示"打开了一个网络链接",而打开一个Soc ...