转载请标注 http://blog.csdn.net/zgh1988/article/details/7863888
#include <stdio.h>
int main()
{
printf("Hello World");
return 0;
}
这是一个“Hello World”程序,相信大家对它再熟悉不过了。现在就让我们来探讨该程序是如何输出“Hello World”,在控制台输出“Hello World”的背后,是谁在为它付出?
1、如何产生一个可运行程序
在Linux环境下,我们可以使用GCC编译Hello World程序,只需要
gcc hello.c
./a.out
上述过程可以分解为4个步骤来完成,分别是预处理(pre-processing)、编译(Compilation)、汇编(Assembly)、链接(Linking)。如下图所示:
(1)预处理
C语言的预处理主要由三方面的内容:
1 宏定义; #define PI 3.1415
2 文件包含; #include<stdio.h> #include "hello.h"
3 条件编译; #if #ifdef #elif #else #endif
4 删除所有的注释
Linux环境下的预处理命令:
gcc -E hello.c -o hello.i
(2)编译
编译过程就是把预处理完的文件进行一系列的 词法分析, 语法分析, 语义分析, 中间代码的生成及优化, 目标代码的生成与优化。
Linux环境下的编译命令:
gcc -S hello.i -o hello.s
(3)汇编
汇编器是将汇编代码转变成机器可以执行的指令,每一个汇编语句几乎都对应着一条机器指令。
linux环境下的汇编命令:
as hello.s -o hello.o
gcc -c hello.s -o hello.o
(4)链接
随着程序的复杂性越来越高,我们不得不将程序分成不同的模块来进行独立的编译。而链接器的主要任务就是使各个模块之间能够正确的衔接在一起,形成可运行程序。链接过程主要包括了 地址和空间分配,符号解析,重定位等步骤。
2、编译过程是怎样?
如下图所示:
下面我们将以 y = ( x + 4 ) * ( 2 + 6 )来分析这个过程
(1)词法分析
词法分析阶段,就是利用扫描器(Scanner)对源程序进行遍历,识别出源程序中的各个记号(token)。源程序中常见的记号可以分为以下几大类:
1 关键字; 保留字
2 标识符; 变量名,函数名 x, y,z
3 字面量; 常数和字符串常量 60 、“zgh”
4 特殊符号; + - * / ;
y = ( x + 4 ) * (2 + 6 )
(2)语法分析
对词法分析产生的记号进行语法分析,从而产生语法树(Syntax tree)。
y = ( x + 4 ) * (2 + 6 )
(3)语义分析
语义分析器根据语义规则对语法树中的语法单元进行静态语义检查,如类型检查和转换。比如讲一个浮点型赋值给一个指针的时候,语义分析程序会发现这个类型不匹配,编译器将会报错。在语义分析阶段,编译器所能分析的语义是静态语义。
y = ( x + 4 ) * (2 + 6 )
(4)中间代码的生成
源代码优化器往往会将整个语法树转换成中间代码(Intermediate Code)。
常见的中间代码有:三地址码。
例如:
t1 = 2 + 6;
t2 = x + 4;
t3 = t2 * t1;
y = t3;
优化之后:
t2 = x + 4
t2 = t2 * 8
y = t2
(5)目标代码生成与优化