当我们进行编译的时候,要使用一系列的工具,我们称之为工具链,其中包括:
预处理器cpp
编译器gcc/g++
汇编器as
链接器ld
一个C/C++程序编译过程包括下面几个阶段:
预处理 预处理器cpp将对源文件中的宏进行展开。
编译 gcc将c文件编译成汇编文件。
汇编 汇编器as将汇编文件编译成机器码。
链接 链接器ld将目标文件和外部符号进行连接,得到一个可执行二进制文件。
下面以一个很简单的 hello.c 来探讨这个过程
......
#include <stdio.h>
#define BUFSIZE 1024
int main(int argc, char *argv[])
{
char hello[BUFSIZE] = "Hello my friend!";
printf("%s\n", hello);
return 0;
}
......
### 预处理(预处理器 cpp)
[butbueatiful@xt myhello]$ gcc -E hello.c -o hello.i
或
[butbueatiful@xt myhello]$ cpp hello.c -o hello.i
我们用vi打开 hello.i 可以看到有如下内容:
......
int main(int argc, char *argv[])
{
char hello[1024] = "Hello my friend!";
printf("%s\n", hello);
return 0;
}
......
我们可以看到,文件中宏定义 BUFSIZE 出现的位置被 1024 替换掉了,其它的内容保持不变。
### 编译器将 .i 文件编译成汇编文件
[butbueatiful@xt myhello]$ gcc -S hello.i # 得到汇编文件hello.s
### 汇编器将汇编文件编译成机器码(汇编器 as)
[butbueatiful@xt myhello]$ gcc -c hello.s -o hello.o
或
[butbueatiful@xt myhello]$ as hello.s -o hello.o
hello.o中为目标机器上的二进制文件
用 nm 查看文件中的符号:
[butbueatiful@xt myhello]$ nm -a hello.o
00000000 b .bss
00000000 n .comment
00000000 d .data
00000000 n .note.GNU-stack
00000000 t .text
00000000 a hello.c
00000000 T main
U puts
既然已经是二进制目标文件了,能不能执行呢?
[butbueatiful@xt myhello]$ chmod +x hello.o
[butbueatiful@xt myhello]$ ./hello.o
-bash: ./hello.o: cannot execute binary file
其实这时 puts 前面的 U 表示这个符号的地址还没有定下来,T表示这个符号属于代码段。ld连接的时候会为这些带U的符号确定地址。
### 链接(链接器 ld)
链接需要指定库的位置。通常程序中会有很多的外部符号,因此需要指定的位置就会很多。
不过,我们只需要调用 gcc 即可,ld会自己去找这些库的位置。
[butbueatiful@xt myhello]$ gcc hello.o -o hello # 得到可执行文件hello