回顾链接这一章
我对链接的理解
可以将一个大型的源文件分解成更小的模块,其目的是更加易于管理,修改和编译这些模块。
将一个大型文件分离后肯定是会产生问题的
1.符号多重定义
要理解符号多重定义,那么就先得明白什么是符号,什么是符号定义,什么是符号解析,什么是强符号,什么是弱符号。
符号: csapp中是这样定义符号,每一个符号对应每一个函数,每一个全局变量和每一个静态变量。
int a;
void b();
statc int c;
// 这些都是符号
符号定义: 对该符号进行解释/定义,既为符号定义。严格来说每个符号都必须定义,但是未初始化变量一般编译器都会给予默认值,这个过程默认定义了这些未初始化的变量。符号定义不一定需要符号,在编译的时候只会警告,但是最好都加上,这样比较符合逻辑,有符号才有符号定义。函数
和未定义的
符号和符号定义可以是多对一的关系,可以好几个在不同模块的符号对应到同一个模块的符号定义。
int a = 1;
void b(){};
statc int c = 1;
// 这些都是符号定义
符号解析: 有了符号定义才有符号解析,符号解析就是将每个符号和它的定义一一对应起来。(同时编译器还将每个引用都链接到定义处,确定运行时符号的位置,当然具体还是得去看《csapp》里 7.7 重定位这节,更加详细。)
如上图,void foo(void)只是个符号,缺少定义。因此编译器就无法解析该符号,因为找不到定义;
符号引用: 顾名思义,就是我使用了/引用了这个符号,就叫符号引用。一般来说,每个局部符号都对应着一个定义,当这个符号被引用时,编译器才会去做符号解析这个事情。就例如上图,我们将代码改动一下。
void foo(void);
int main()
{
// foo(); 注释掉了foo()
return 0;
}
这样编译器就不会报错了,因为我们并没有去引用这个符号,自然不会去解析这个符号,不去解析符号,那么也就不需要去理会这个符号有没有定义了。
强符号: 函数和已定义的全局变量,称为强符号。
弱符号: 未定义的全局变量,成为弱符号。
明白了以上几点,就知道如何解决多重符号定义的问题了。
通常来说我们一般都把符号写在头文件中,把符号定义写在源文件中,但在早期的c中是没有头文件的,
头文件#include,它的作用只是嵌入一段代码的作用,发生在编译时期。
有空再来整理整理吧,只是想到啥写啥。