Author: Once Day Date:2023年1月11日
漫漫长路,有人对你微笑过嘛…
参考引用文档:
如果在代码里面重复定义变量、全局函数,那么编译器就会报出redefinition of 'xxx'
的错误。
注意,这里针对同一作用域才会有冲突。C代码的作用域是按两种情况分类,如下:
那这里重点关注重定义问题,就是全局作用域。如下面的定义:
int a;
int func(void) {
....
}
一般而言,在同一作用域下不能定义同一个变量或函数。
但是GNU C对标准C语言进行了扩展,在GCC中,对于符号(在编译时,变量和函数都被抽象成符号)而言,存在着强符号和弱符号之分。
对于C/C++
编译器而言,默认函数和已初始化的全局变量为强符号,而未初始化的全局变量为弱符号。
可以使用__attribute__(weak))
来声明一个符号为弱符号。
如果gcc
发现几个相同的变量,并不会直接报错,而是按下面的规则进行取舍:
redefinition of "xxx"
。一个简单强弱符号例子如下:
int x;
int x = 1;
可以看成函数声明,第一个x
是弱符号,第二个x
是强符号,因此编译器取值为x=1
。
int x __attribute__((weak));
float x ;
如这样的使用,也会报错,提示类型复杂。一般该属性对于函数声明使用较多。
强弱符号一般用于库的编译和多文件编译过程,一般用来做桩(stub)函数。
下面是一个多文件编译的例子:
//symbol.c
#include
void __attribute__((weak)) func(void) {
printf("this is func_a.\n");
}
int main(void) {
printf("hello world\n");
func();
}
//symbol2.c
#include
void func(void) {
printf("this is func_b.\n");
}
makefile文件如下:
symbol : symbol.c symbol2.c
gcc -o symbol symbol.c symbol2.c
命令行输出如下:
ubuntu->c-code:$ make symbol
gcc -o symbol symbol.c symbol2.c
ubuntu->c-code:$ ./symbol
hello world
this is func_b.
可以看到最终符号链接的对象是symbol2.c
中的强符号,对于库来使用也是一样。
这个主要是针对声明使用。编译器在编译阶段只负责将源文件编译成目标文件,即二进制文件,然后由链接器对所有二进制文件进行链接操作。
在分离式编译中,当编译器检查到当前使用的函数或者变量在本模块中仅有声明而没有定义时,编译器直接使用这个符号,将工作转交给链接器,链接器则负责根据这些信息找到这些函数或者变量的实体地址,因为在程序执行时,程序必须确切地知道每个函数和全局变量的地址。如果没有找到该符号的实体,就会报undefined reference错误,这种符号之间的引用被称为强引用.
编译器默认所有的变量和函数为强引用,同时编程者可以使用__attribute__((weakref))来声明一个函数,注意这里是声明而不是定义,既然是引用,那么就是使用其他模块中定义的实体,对于函数而言,我们可以使用这样的写法:
__attribute__((weakref)) void func(void);
然后在函数中调用func(),如果func()没有被定义,则func的值为0,如果func被定义,则调用相应func。
在现代的编译器中,有如下的规定:
即如下的使用方式:
static __attribute__((weakref("test"))) void weak_ref(void);
下面是一个例子来说明这点:
//symbol.c
#include
static void __attribute__((weakref("func_b"))) func(void);
int main(void)
{
printf("hello world\n");
if (func) {
func();
} else {
printf("func is null.\n");
}
}
直接编译运行上面文件,有:
ubuntu->c-code:$ gcc -o symbol symbol.c;./symbol
hello world
func is null.
说明当前func_b
没有定义的实体,因此引用地址为0
。
加上下面文件:
//symbol2.c
#include
void func_b(void)
{
printf("this is func_b.\n");
}
然后编译运行:
ubuntu->c-code:$ gcc -o symbol symbol.c symbol2.c;./symbol
hello world
this is func_b.
这个时候,func
就找到对应的定义实体了。
弱符号的机制是用来定义某个桩函数,适合于用户自定义新函数来覆盖原有的功能。
弱引用的机制是用来预先定义某个扩展功能桩函数,用户需要时再进行实际的实体定义,如果不定义,也不会报错。