我们已经知道,每个源文件都是单独经过编译器处理生成对应的目文件。
test.c
经过编译器处理生成
test.o
add.c
经过编译器处理生成
add.o
我们在
test.c
的文件中使用了
add.c 文
件中的
Add
函数和
g_val
变量。
我们在
test.c 文
件中每?次使用?
Add
函数和
g_val
的时候必须确切的知道
Add
和
g_val
的地
址,但是由于每个文件是单独编译的,在编译器编译
test.c
的时候并不知道
Add
函数和
g_val
变量的地址,所以暂时把调用
Add
的指令的目标地址和
g_val
的地址搁置。等待最后链接的时候由链接器根据引用的符号 Add
在其他模块中查找
Add
函数的地址,然后将
test.c
中所有引用到
Add
的指令重新修正,让他们的目标地址为真正的
Add
函数的地址,对于全局变量
g_val
也是类
似的方法来修正地址。这个地址修正的过程也被叫做:重定位。
前面我们非常简洁的讲解了?个C的程序是如何编译和链接,到最终生成可执行程序的过程。
其实很多内部的细节无法展开讲解。比如:目标文件的格式elf,链接底层实现中的空间与地址分配,符号解析和重定位等,如果你有兴趣,可以看《程序的自我修养》?书来详细了解。
三:运行环境
1.
程序必须载入内存中。在有操作系统的环境中:?般这个由操作系统完成。在独立的环境中,程序的载?必须由手工安排,也可能是通过可执行代码置入只读内存来完成。
2.
程序的执行便开始。接着便调用main函数。
3.
开始执行程序代码。这个时候程序将使用?个运行时堆栈(stack),存储函数的局部变量和返回地址。程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程?直保留他们的值。
4.
终止程序。正常终?main函数;也有可能是意外终止。