在标准C的任何一种实现中,存在两个不同的环境。
- 翻译环境:C语言代码转化成二进制的指令(在可执行程序内部)。
- 执行环境:执行二进制的代码。

Add.c:
int Add(int a, int b)
{
return a + b;
}
Sub.c:
int Sub(int a, int b)
{
return a - b;
}
main.c:
extern int Add(int, int);
extern int Sub(int, int);
int main()
{
int a = 10;
int b = 5;
printf("%d\n", Add(a, b));
printf("%d\n", Sub(a, b));
return 0;
}
把C语言代码翻译成可执行程序经历了两个过程:编译+链接。
- 编译又分为三个阶段:预处理 + 编译 + 汇编。
- 链接就只有一步。
预处理
gcc 源文件名 -E -o 生成的文件名。
- 注释的删除。
- 头文件的展开。
#define的替换。

编译
gcc -S xxx.i或者gcc -S 源文件名
这两种方式都会生成一个.s的汇编文件。这个过程是把C语言代码翻译成了汇编代码。比如符号汇总。

汇编:
gcc -c xxx.s或者gcc -c 源文件名:
最终都会生成一个.o的目标文件。在Windos环境下的目标文件是以.obj结尾。而在Linux环境下的目标文件是以.o结尾。目标文件中放的都是二进制指令。
这个阶段主要是把汇编代码翻译成了二进制指令。比如形成符号表。

链接:
gcc xxx.o -o 文件名或者gcc 源文件 -o 生成的文件。
最终生成的文件也是二进制的。这个阶段主要做了:1、合并段表 2、 符号表的合并和符号表的重定位。
Linux环境下,使用gcc编译产生的目标文件xxx.o和可执行程序gcc -o 产生的文件,都是按照ELF这种文件格式存储的。
使用readelf 工具能够识别elf格式的文件。
使用readelf 文件名 -s即可查看符号表。
test.c
#include <stdio.h>
int g_val = 100;
extern int Add(int x , int y);
int main()
{
int a = 1;
int b = 1;
int c = Add(a, b );
printf("%d\n", c);
return 0;
}
add.c
int Add(int x, int y)
{
return x + y;
}
makefile
test:test.o add.o
gcc -o test test.o add.o
test.o:test.c
gcc -c test.c -o test.o
add.o:add.c
gcc -c add.c -o add.o
.PHONY:clean
clean:
rm -f *.o test

链接之后的可执行程序的符号表
主要就是将多个目标文件中的符号表给合并在一起。

这一块的更多知识请看<<程序员的自我修养>>!!!