编译和链接

一个C程序最后生成可执行目标文件,会分阶段经过预处理编译汇编链接的过程,而往往整个过程由IDE提供的编译驱动程序, 全权代表用户调用预处理器,编译器,汇编器和链接器。

预编译
/*  test.c  */
#include 
#define pai 3
// this is conment
int main()
{
    Element i = 10;
    Element result = pai * 3;
    printf("%d\n", result);
}

使用gcc -E test.c -o test.i,或者cpp test.c > test.i命令。表示 test.c 文件被预编译成一个 test.i 文件。打印test.i的内容是:

 ......还有头文件等信息已经省略
typedef int Element;
int main()
{
     Element i = 10;
     Element result = 3 * i;
     printf("%d\n", result);
}

发现预处理器在这里只做三件事:

  • 去掉注释
  • 展开所有宏定义
  • 处理#include预编译指令,复制头文件,这个过程是递归进行的。

除此之外,一般预处理器还做:

  • 处理所有的条件预编译指令:#if #ifdef #elif #else #endif
  • 保留所有#pragma编译器指令
编译

编译过程就是把预处理完的文件进行一系列词法分析,语法分析,语义分析及优化后生成相应的汇编代码文件。
现代版本的GCC把预编译和编译两个步骤合并成一个步骤,使用一个叫做ccl的程序来完成这个步骤。

  • 词法分析

    array[index] = (index + 4) * (2 + 6)
    

    源代码程序被输入到扫描器,扫描器的任务很简单,它只是简单的进行词法分析,运用一种有限状态机的算法将源代码的字符序列分割成一系列的记号。

    词法分析的记号一般分为:关键字,标识符,字面量和特殊符号。

  • 语法分析
    语法分析器将对由扫描器产生的记号进行语法分析,从而产生语法树。由语法分析器产生的语法树是以表达式为节点的树。

  • 语义分析
    语法分析仅仅是完成了对表达式的语法层面的分析,但是它并不能了解这个语句是否真正的意义。比如:C语言中两个指针做减法是没有意义的,但是这个语句在语法上是合法的。
    编译器所能分析的是静态语义,所谓静态语义是指在编译器可以确定的语义,与之对应的是动态语义,指只有在运行期才能确定的语义。
汇编

汇编器将汇编代码转变成及其可以执行的指令,每一个汇编语句几乎都对应一条机器指令。

链接

在一个程序被分割成多个模块之后,这些模块之间如何组合形成一个单一的程序是必须解决的问题,模块之间如何组合的问题可以归结为模块之间如何通信的问题。
最常见的属于静态语言的C/C++模块之间通信的两种方式:一种是模块间的函数调用,另外一种是模块间的变量访问。
函数访问必须知道目标函数的地址,变量访问必须知道目标变量的地址。所以两种方式都可以归结为一种方式:那就是模块间符号的引用。完成模块间符号引用的过程就是链接。

/* main.c */
#include 

extern num1;

int main()
{
    int num2 = 10;
    printf("%d + %d = %d\n", num1, num2, add(num1, num2));
}

/* foo.c */
int num1 = 20;

int add(int num1, int num2)
{
    return num1 + num2;
}

使用gcc main.o foo.o -o a.out链接多个可重定位目标文件生成可执行目标文件,整个过程如图:

编译和链接_第1张图片
image.png

gcc常用编译选项
预处理:
  gcc -E test.c -o test.i

编译生成汇编代码:
  gcc -S test.i -o test.s

汇编器生成可重定位目标文件:
  gcc -c test.s -o test.o

链接器生成可执行目标文件:
  gcc test.o -o test.out

显示警告:
  gcc -Wall main.c -o a.out

链接静态库:
  gcc main.c -o c.out -l pthread

定义宏:
#include 
int main(int argc, char **argv)
{
    #ifdef MARCO
        printf("MARCO defined\n");
    #endif
}
gcc -DMARCO="hello" main.c
输出:MARCO defined

你可能感兴趣的:(编译和链接)