编译和链接


前阵子看了一下《程序员的自我修养:链接、装载与库》,觉得还是有一些收获,做了一些笔记,记录一下。

编译和链接

首先是一个hello world,这是一个最基础的程序,如下:

1
2
3
4
5
#include <stdio.h>
int main(){
printf("Hello, world");
return 0;
}

通过gcc编译:

1
2
gcc hello.c
./a.out

就可以输出字符串了。这其实是封装好的过程,实际的过程包括了:

  • 预处理
  • 编译
  • 汇编
  • 链接

具体过程如下:

预处理

首先是源代码文件hello.c和相关的头文件,如stdio.h被预编译器cpp编译成一个.i文件。对于C++预编译的扩展名是.ii。第一步的过程如下:

1
gcc -E hello.c -o hello.i

打开hello.i,其中有处理后的C代码。主要的处理如下:

  • 将所有的”#define”删除,展开宏定义
  • 处理所有的条件编译指令,比如”#if”,”#ifdef”,”#elif”,”#else”,”#endif”。
  • 处理#include文件,将被包含的文件插入到该预编译指令的位置。这里是递归进行
  • 删除所有注释
  • 添加行号和文件名标识,以便于编译器产生调试用的行号信息等
  • 保留所有的#pragma指令,编译器需要使用它们

编译

在这里就使用编译原理学过的词法分析,语法分析,语义分析,然后优化后生成对应的汇编代码文件

现代版本的gcc把预编译和编译用ccl来完成,也可以用

1
gcc -S hello.c -o hello.s

汇编

汇编器将汇编代码转化为二进制指令,用as来完成

1
as hello.s -o hello.o

或者

1
gcc -c hello.s -o hello.o

也可以用gcc从源代码开始:

1
gcc -c hello.c -o hello.o

链接

将汇编生成的目标文件用ld链接起来,生成可执行文件。

静态链接是一种模块的拼装。

  • 地址和空间分配
  • 符号决议
  • 重定位