符号解析
符号解析的目的就是将各个模块(模块之中与模块之间)引用与定义联系起来
首先,什么是符号?符号包括
函数(static和非static) |
---|
全局变量 |
外部变量 |
静态局部变量 |
static属性
先简单说明一下static属性,对于全局变量和函数来说,使用static是将它们的“可见”范围限制在了本模块,即它们可以被合法引用的地方仅仅限与定义它们的这个模块(文件);
而对于函数体内部的过程变量来说,使用static修饰是延长了它的“生存周期”,即使在退出循环也不会被销毁。
具体到存放方式就是:符号都是存放在堆里面,而不是和局部变量一样放在栈里面
符号解析
在整个c语言程序之中,既有符号的定义又有符号的引用(使用)。那么,在链接过程之中,真正链接之前需要做的一件事就是将将这些引用与定义关联起来
进一步,试想如果C语言源程序中如果同一个符号名被定义了多次,链接又该如何进行呢?
首先,如果是局部符号(带static属性),由于作用范围限制在同一模块之中,所以它的引用解析是很是清晰的,只需要在同一模块之中不被重复定义即可。
麻烦的是全局符号(非static的全局变量和函数)的解析,如果发生同名的现象链接器又会做出什么样子的反应呢?
首先说明一下强弱符号,强弱符号的概念是针对全局符号的概念:
强符号:函数名、已初始化的全局变量
弱符号:未初始化的全局符号
由此在强弱符号概念的基础之上,链接器在链接时对于同名全局符号有以下规则:
- 链接器不允许多个同名的强符号
- 当强符号和弱符号同名时,选择强符号作符号解析
- 当弱符号之间同名时,随机选择其中一个作符号解析
接下来看一个强弱符号同名的例子
mismatch-main.c
long int x; /* Weak symbol */
int main(int argc, char *argv[]) {
printf("%ld\n", x);
return 0;
}
mismatch-variable.c
/* Global strong symbol */
double x = 3.14;
编译
gcc -c mismatch-variable.c mismatch-main.c
静态链接
gcc -static -o prog mismatch-main.o mismatch-variable.o
运行
./prog
以下是机器操作过程与运行结果
分析:
在mismatch-variable.c定义了一个强符号浮点数x=3.14(8字节) 而在mismatch-main.c定义的是一个同名的未初始化的弱符号长整型数x; 根据上文所述的规则2,在两模块链接过程中,对于“x”选择的解析是mismatch-variable中的浮点数x为此我们运行下面一段代码:
print-double.c
#include <stdio.h>
int main(){
double x = 3.14;
unsigned short *p=(unsigned short *)&x;
for(int i = 0;i < 4; i++){
printf(" [%d] %p :%x\n",i+1,p,*p);
p++;
}
return 0;
}
结果:
我们把它转变成十进制数:
发现所得的数也正好与前面prog所得结果461,425,307,021,498,087相对应
所以prog中main函数是将浮点数x用作x的最终解释,并且将3.14的double型表示按照长整型输出了
对于prog中的错误在编译链接过程中都不会报错,所以一旦编程过程中出现这种错误是比较难以发现的,只有运行之后才会发现错误,但是在代码量巨大的情况下,定位错误依然有难度很大。因此作为程序员应该尽量细心避免这种错误。
在此的建议是:
1.尽量不用跨模块全局变量,给全局变量带上static属性
2.非得使用跨模块引用记得带上外部属性