编译过程(从编译到链接)

当我们写完代码编译的时候,计算机都进行了哪些步骤呢?这些步骤又都有些什么作用呢?

一、执行一个程序的过程

当我们编写一个程序并编译执行,如下(hello.c)

#include <stdio.h>
int main()
{
    printf("Hello World
");
    return 0;
}
/*编译执行*/ $gcc hello.c
-o hello.out $./hello.out Hello World

那么在其中执行了如图的过程:

其中的主要过程包括:

预处理(Propressing):处理"#define、#include、#if……"等,删除注释,添加行号,但保留所有#pragma编译器指令;

编译(Compilation):词法分析、语法分析、语义分析及优化后产生汇编代码文件;

汇编(Assembly):将汇编代码转换成机器可以执行的指令;

链接(Linking):将所有需要的静态、动态库、文件等链接成为可执行程序。

二、编译过程

 也就是上图中的编译部分,主要过程共分为6步,如下图:

扫描(Scanner):用类似于有限状态机,把代码分割成一系列记号(Token);

语法分析(Parser):通过对记号进行分析,生成语法树(Syntax Tree);

语义分析(Semantic Analyzer):完成表达式语法层面的分析,并不关心意义;如不会处理两个指针相乘;

源代码优化(Source Code Optimizer):将语法树转换成中间代码,如三地址码 x = y op z;

代码生成(Code generator): 初步生成目标代码;

目标代码优化(Code Optimizer):将目标代码优化为最终的代码。比如选择合适的寻址方式、使用位移来代替乘法运算、删除多余指令等。

三、在各个步骤中使用的编译命令

用下面的程序举例子

#include<stdio.h>
#define MAX 100
int main()
{
/*I am only the test*/
printf("Hello World!");
printf("%d
",MAX);
return 0;
}

gcc -E hello.c -o hello.i //后可以看到宏定义和注释都没有了

gcc -S hello.i -o hello.s //后可以看到被编译成的汇编文件。

gcc -c hello.s -o hello.o //将汇编文件生成目标文件

以上三个步骤可以用一句话代替: gcc -c hello.c -o hello.o 这样可以直接生成目标文件。

最后链接成可执行文件有点麻烦:
ld -static /usr/lib/crt1.o /usr/lib/crti.o /usr/lib/gcc/i486-linux-gnu/4.1.3/crtbeginT.o -L/usr/lib/gcc/i486-linux-gnu/4.1.3 -L/usr/lib -L/lib hello.o --start-group -lgcc -lgcc_eh -lc --end-group /usr/lib/gcc/i486-linux-gnu/4.1.3/crtend.o /usr/lib/crtn.o

当然各个文件在不同系统中的路径也不一定相同,如我在centos6.6就是下面的结果:
ld -static /usr/lib64/crt1.o /usr/lib64/crti.o /usr/lib/gcc/x86_64-redhat-linux/4.4.4/crtbeginT.o -L/usr/lib/gcc/x86_64-redhat-linux/4.4.4 -L/usr/lib64 -L/lib64 hello.o --start-group -lgcc -lgcc_eh -lc --end-group /usr/lib/gcc/x86_64-redhat-linux/4.4.4/crtend.o /usr/lib64/crtn.o

原文地址:https://www.cnblogs.com/bugutian/p/5087577.html