GCC编译C源码有四个步骤:

预处理-----> 编译 ----> 汇编 ----> 链接

一、 编译和链接的流程

C语言的编译链接过程要把我们编写的一个c程序(源代码)转换成可以在硬件上运行的程序(可执行代码),需要进行编译和链接。编译就是把文本形式源代码翻译为机器语言形式的目标文件的过程。链接是把目标文件、操作系统的启动代码和用到的库文件进行组织形成最终生成可执行代码的过程。过程图解如下:


从图上可以看到,整个代码的编译过程分为编译和链接两个过程,编译对应图中的大括号括起的部分,其余则为链接过程。

下面是对应的GNU工具链生成文件的过程:

说明:这些后缀并不是必须的,这只是常见的后缀方式,对于C++,对应一般为.cpp,.ii,.s,.o,exec/.so等。参考http://zhidao.baidu.com/question/89958523.html。当然,本质上,.c/.i/.s文件都是文本文件,可以直接查看内容的。.o/exec/.so等文件需要工具查看一些信息。

二、. 编译的三个阶段:

1. 预处理阶段
在正式的编译阶段之前进行,预处理阶段将根据已放置在文件中的预处理指令来修改源文件的内容。预处理处理的主要内容包括:
A、宏定义,如#define PI 3.14
简单来说就是进行宏替换。
B、条件编译,如#ifdef,#ifndef,#else,#endif等等
预编译程序根据这些指令,将不必要参与编译的代码过滤掉。
C、头文件包含,如#include <filename.h>, #include "filename.h"
D、特殊符号,预编译程序可以识别一些特殊的符号
典型的就是__LINE__、__FILE__等编译器内置的预定义宏了。

总之,预处理的核心工作就是“替换”。当然,预处理也会去掉代码中的注释内容,总之,预处理的目的就是简化编译阶段扫描的内容。

对应的GCC选项:-E(预处理,但不编译),输出为对.c文件预处理后的结果,默认输出到控制台,使用-o指定输出到文件。对于GNU工具链,其提供了独立的预处理器,为cpp

举例如下:

// File: test.h
#define MACRO_A 1
int foo();
// File: test.cpp
//#include <stdio.h>
#include "test.h" #define MACRO_B "macro_B" int main()
{
printf("MACRO_A is :%d; MACRO_B is: %s\n", foo(), MACRO_B);
return 1;
} int foo()
{
return MACRO_A;
}

编译:

gcc test.cpp -E -o out.ii

说明:这里的test.cpp中,注释了#include <stdio.h>进行预处理是不会报错的,只有在编译的时候才会提示printf错误。下面是输出的out.ii的内容:

# 1 "test.cpp"
# 1 "<built-in>"
# 1 "<命令行>"
# 1 "test.cpp" # 1 "test.h" 1 int foo();
# 4 "test.cpp" 2 int main()
{
printf("MACRO_A is :%d; MACRO_B is: %s\n", foo(), "macro_B");
return 1;
} int foo()
{
return 1;
}

说明:这里可以看到,里面有一些#开头的信息,这些信息在编译阶段是会忽略(不会被扫描)的,这些行的内容是一些记录一些行信息和文件的信息等等。之所以上面把#include <stdio.h>注释掉,是因为加入这一句之后,stdio.h中的内容也会被替换到out.ii中,这样内容太多,不适合贴在这里。

如果使用cpp进行预处理,那么就是(GCC内部还是调用cpp处理的):

cpp test.cpp -o out.ii

2. 编译和优化
经过预编译得到的输出文件中,只有常量;如数字、字符串、变量的定义,以及C语言的关键字,如main,if,else,for,while,{,}, +,-,*,\等等。
编译就是指传统的编译原理中提到的内容了,词法分析、语法分析、中间代码生成、汇编代码生成等。(说明:这里的中间代码生成是编译原理中的中间表达式的代码,不是.o目标文件,好像有时候也把目标文件称之为中间文件,这里区分一下)
优化也是编译原理中涉及的内容,优化涉及的内容很多,优化可以是对中间代码本身的优化或者目标代码的生成进行(即由中间代码生成汇编代码的过程或者生成目标文件的过程),关于优化,这里不深入探讨。
总之,这里说的编译的过程,就是由预处理的文件,编译优化得到汇编文件的过程。

对应的GCC选项:-S(编译,但不汇编)。输出为.s文件。说明:gcc编译可以以源文件作为输入,其实也是可以直接用预处理的.i文件作为输入的。当然,唯一要注意的是如果原来的源文件为cpp而输出是.i不是.ii,那么.i作为输入的时候,最好使用g++,否则可能编译有问题(gcc/g++会根据文件后缀判断是c还是c++的文件,所以对应就好了)。

对于GNU工具链,其提供了独立的预编译器,为ccl(好像没有这个命令呢?总之,ccp和ccl几乎不会用到,直接用gcc就可以了)。

还是上面的例子(把#include<stdio.h>取消注释):

$ gcc test.cpp -E -o test.ii
$ gcc test.cpp -S
$ gcc test.ii -S
$

说明:其中-S默认输出到文件中,所以不使用-o也是可以输出到文件中的。

3. 汇编

汇编实际上指把汇编语言代码翻译成目标机器指令的过程。其输出就是目标文件了(.o)。目标文件中所存放的也就是与源程序等效的目标的机器语言代码。目标文件由段组成。通常一个目标文件中至少有两个段:
代码段:该段中所包含的主要是程序的指令。该段一般是可读和可执行的,但一般却不可写。
数据段:主要存放程序中要用到的各种全局变量或静态的数据。一般数据段都是可读,可写,可执行的。

说明:汇编之后,编译的过程就完成了,得到了目标文件(.o)。

对应的GCC选项:-c(汇编,但不链接)

还是上面的例子(用.ii/.s/.cpp作为输入都是可以的,gcc会根据后缀知道输入是什么文件,从而在此基础上继续处理):

$ gcc test.ii -c
$ gcc test.s -c
$ gcc test.cpp -c
$

其中gcc test.s -c,就是直接编译汇编文件,而gcc test.cpp -c就会从预处理开始进行处理了。对于GNU工具链,其提供了独立的汇编器,为as。也可以使用as编译汇编文件(当然,也只能编译汇编文件了),上面的gcc test.s -c相当于:

$ as test.s -o test.o

(不使用-o test.o,那么输出默认为a.o)

总结:编译主要包括预处理、编译、汇编三个阶段,通过GCC的选项能控制GCC处理到某一步之后就停止,当然,实际的GCC处理,可能不一定是完全的一步一步的处理的,可能会有一些优化的处理方式。

三、链接

由汇编程序生成的目标文件并不能立即就被执行,其中可能还有许多没有解决的问题。
例如,某个源文件中的函数可能引用了另一个源文件中定义的某个符号(如变量或者函数调用等);在程序中可能调用了某个库文件中的函数,等等。所有的这些问题,都需要经链接程序的处理方能得以解决。
链接程序的主要工作就是将有关的目标文件彼此相连接,也即将在一个文件中引用的符号同该符号在另外一个文件中的定义连接起来,使得所有的这些目标文件成为一个能够诶操作系统装入执行的统一整体。

(1)静态链接
在这种链接方式下,函数的代码将从其所在地静态链接库中被拷贝到最终的可执行程序中。这样该程序在被执行时这些代码将被装入到该进程的虚拟地址空间中。静态链接库实际上是一个目标文件的集合,其中的每个文件含有库中的一个或者一组相关函数的代码。
 (2) 动态链接
在此种方式下,函数的代码被放到称作是动态链接库或共享对象的某个目标文件中。链接程序此时所作的只是在最终的可执行程序中记录下共享对象的名字以及其它少量的登记信息。在此可执行文件被执行时,动态链接库的全部内容将被映射到运行时相应进程的虚地址空间。动态链接程序将根据可执行程序中记录的信息找到相应的函数代码。

对于可执行文件中的函数调用,可分别采用动态链接或静态链接的方法。使用动态链接能够使最终的可执行文件比较短小,并且当共享对象被多个进程使用时能节约一些内存,因为在内存中只需要保存一份此共享对象的代码。但并不是使用动态链接就一定比使用静态链接要优越。在某些情况下动态链接可能带来一些性能上损害。

四、整体过程

下面是整体的过程:

解释型语言:

相对于编译型语言存在的,源代码不是直接翻译成机器语言,而是先翻译成中间代码,再由解释器对中间代码进行解释运行。比如Python/JavaScript / Perl /Shell等都是解释型语言。
 
解释型语言:程序不需要编译,程序在运行时才翻译成机器语言,每执 行一次都要翻译一次

C语言编译过程的更多相关文章

  1. C语言编译过程及数据类型

    写在前面 C语言可以称得上是高级语言中的低级语言,接下来一段时间,我会写一下文章关于c语言,把它的神秘面纱一 一揭开.下面主要是c语言的C语言编译过程及数据类型 源文件编译过程 为了使计算机能执行高级 ...

  2. C语言编译过程以及gcc编译参数

    1.1       C语言编译过程,gcc参数简介 1.1.1          C语言编译过程 一.gcc - o a a.c -o:指定文件输出名字 二.C语言编译的过程: 1.1.1       ...

  3. 转 C语言编译过程简介

    C语言编译过程简介 C语言编译过程简介 刚开始接触编程的时候,只知道照书敲敲代码,一直都不知道为什么在windows平台下代码经过鼠标那样点击几下,程序的结果就会在那个黑色的屏幕上.现在找了个机会将C ...

  4. GCC 使用-C语言编译过程

    任何一种高级语言,要想在机器上执行,必须翻译为机器能读懂的机器语言.编译器就相当于翻译官,将高级语言翻译为机器语言. GCC 最初只用了编译 C 语言程序,全称是 GNU C Compiler.后来扩 ...

  5. Go 语言编译过程

    走进Golang之编译器原理_大愚Talk-CSDN博客 https://blog.csdn.net/hel12he/article/details/103061921 go编译器 - 知乎 http ...

  6. go语言编译过程概述

    go语言编译过程概述 总结自<go语言设计与实现> 名词解释: 中间代码 中间代码是编译器或者虚拟机使用的语言,它可以来帮助我们分析计算机程序.在编译过程中,编译器会在将源代码转换到机器码 ...

  7. C语言编译过程详解

    前言 C语言程序从源代码到二进制行程序都经历了那些过程?本文以Linux下C语言的编译过程为例,讲解C语言程序的编译过程. 编写hello world C程序: // hello.c #include ...

  8. C语言编译过程(转)

    内容摘要 : C语言编译的整个过程是非常复杂的,里面涉及到的编译器知识.硬件知识.工具链知识都是非常多的,深入了解整个编译过程对工程师理解应用程序的编写是有很大帮助的,希望大家可以多了解一些,在遇到问 ...

  9. C语言基础(21)-C语言编译过程及GCC参数简介

    任何C语言的编译过程可分为以下三部分: 一.预编译 在C语言中,以#开头的语句又叫预编译指令.预编译主要做以下两件事情: 1.将#include包含的头文件做简单的文本替换: 2.将代码中的注释删除. ...

随机推荐

  1. 【腾讯Bugly干货分享】QQ电话适配iOS10 Callkit框架

    本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/58009392302e4725036142fc Dev Club 是一个交流移动 ...

  2. ENode 2.0 - 第一个真实案例剖析-一个简易论坛(Forum)

    前言 经过不断的坚持和努力,ENode 2.0的第一个真实案例终于出来了.这个案例是一个简易的论坛,开发这个论坛的初衷是为了验证用ENode框架来开发一个真实项目的可行性.目前这个论坛在UI上是使用了 ...

  3. MySQL数据丢失讨论

    原文地址:http://hatemysql.com/tag/sync_binlog/ 1.  概述 很多企业选择MySQL都会担心它的数据丢失问题,从而选择Oracle,但是其实并不十分清楚什么情况下 ...

  4. Java虚拟机9:Java类加载机制

    前言 我们知道我们写的程序经过编译后成为了.class文件,.class文件中描述了类的各种信息,最终都需要加载到虚拟机之后才能运行和使用.而虚拟机如何加载这些.class文件?.class文件的信息 ...

  5. java线程与并发(二)

    一般而言,线程通常有以下的这么几个状态: 创建状态:准备好了一个多线程操作对象 就绪状态:调用了start()方法,等待CPU调度 运行状态:执行run()方法,正在运行 阻塞状态:暂时停止执行,把资 ...

  6. mongodb java spring data

    关于如何集成spring-data-mongodb到项目中,已经有很多人介绍了,这里只给出几个链接. GETTING STARTED Accessing Data with MongoDB: http ...

  7. IOS 多线程04-GCD详解 底层并发 API

    注:本人是翻译过来,并且加上本人的一点见解. 前言 想要揭示出表面之下深层次的一些可利用的方面.这些底层的 API 提供了大量的灵活性,随之而来的是大量的复杂度和更多的责任.在我们的文章常见的后台实践 ...

  8. 缓存篇(Cache)~第一回 使用static静态成员实现服务器端缓存(导航面包屑)

    返回目录 今天写缓存篇的第一篇文章,在写完目录后,得到了一些朋友的关注,这给我之后的写作带来了无穷的力量,在这里,感谢那几位伙伴,哈哈! 书归正传,今天我带来一个Static静态成员的缓存,其实它也不 ...

  9. JS之BOM

    ECMAScript 是 JavaScript 的核心,但如果要在 Web 中使用 JavaScript,那么 BOM(浏览器对象模型)则无疑才是真正的核心.BOM 提供了很多对象,用于访问浏览器的功 ...

  10. Atitit  J2EE平台相关规范--39个  3.J2SE平台相关规范--42个

    Atitit  J2EE平台相关规范--39个  3.J2SE平台相关规范--42个 2.J2EE平台相关规范--39个5 XML Parsing Specification16 J2EE Conne ...