强引用和弱引用

所谓引用(Reference),是指对符号的使用。在下面的代码中:

int a = 100, b = 200, c;
c = a + b;

第一行是符号定义,第二行是符号引用

  • 目前我们所看到的符号引用,在所有目标文件被链接成可执行文件时,它们的地址都要被找到,如果没有符号定义,链接器就会报符号未定义错误,这种被称为强引用(Strong Reference)
  • 与之相对应的还有一种弱引用(Weak Reference),如果符号有定义,就使用它对应的地址,如果没有定义,也不报错

链接器处理强引用和弱引用的过程几乎是一样的,只是对于未定义的弱引用,链接器不认为它是一个错误,一般默认其为 0(地址为 0),或者是一个特殊的值,以便程序代码能够识别

在变量声明或函数声明的前面加上__attribute__((weak))就会使符号变为弱引用。比如下面这段代码:

#include <stdio.h>
__attribute__((weak)) extern int a;
__attribute__((weak)) extern void func();  //也可以不写extern
int main(){
    printf("&a: %d, func: %d\n", &a, func);
    printf("a = %d\n", a);
    func();
    return 0;
}
  • 我们可以将它编译成一个可执行文件,GCC 并不会报链接错误
  • 但是当程序运行时,输出&a: 0, func: 0后就会发生段错误(Segment Fault),这是因为符号 a 和 func 的地址都为 0,这个地址是禁止访问的

(个人:clion+mingw64的结果为:)

一个改进的例子是:

#include <stdio.h>
__attribute__((weak)) extern int a;
__attribute__((weak)) extern void func();
int main(){
    printf("&a: %d, func: %d\n", &a, func);
    if(&a){
        printf("a = %d\n", a);
    }else{
        printf("a is undefined!\n");
    }
    if(func){
        func();
    }else{
        printf("func() is undefined!\n");
    }
    return 0;
}

运行结果:

&a: 0, func: 0
a is undefined!
func() is undefined!

我使用mingw64的结果仍然build时报错:

(个人

  • 新版本的weakref需要函数别名,别名必须和符号的名字不同,要不然会报错,

  • 若没有找到func1函数,编译不报错,但是会默认func为NULL。若在其他库中定义了func1函数,对func的引用就相当于对func1的引用,func的地址就是func1的地址,此时func1和func等价。
  • 这种弱引用在库的使用上十分有用的。
  • 且必须是static 修饰,此时不能使用extern修饰,好像此时就是引用而不是定义)
#include <stdio.h>
static __attribute__((weakref("a1")))  int a;
static __attribute__((weakref("func1")))  void func();
int main(){
    printf("&a: %d, func: %d\n", &a, func);
    if(&a){
        printf("a = %d\n", a);
    }else{
        printf("a is undefined!\n");
    }
    if(func){
        func();
    }else{
        printf("func() is undefined!\n");
    }
    return 0;
}

 

此时我们将module.c修改为:

运行结果为:

我们再次修改module.c,去掉func1的定义,

 运行结果为:

代码中需要判断的是地址,不是值,所以变量 a 前面需要加&而函数名本身就表示地址,所以 func 前边不需要&

弱引用和强引用非常利于程序的模块化开发,我们可以将程序的扩展模块定义为弱引用,当我们将扩展模块和程序链接在一起时,程序就可以正常使用;如果我们去掉了某些模块,那么程序也可以正常链接,只是缺少了某些功能,这使得程序的功能更加容易裁剪和组合

 应用层的开发,离不开sdk的提供,一般sdk维护了,即使应用没有需求发生,往往也会为了配合sdk,进行简单的修改。以设备升级作为举例,若升级过程中,分为传包(pass),验签(verify),解密(decode),安装(install),上传日志(report)等步骤,并且这些核心接口都是以libsdk.so库的形式提供给应用工程师。那么正常情况下,应用逻辑大致如下:

用户业务流程
...
pass();
...
verify();
...
decode();
...
install();
...
report();
...

但是这样的业务代码,我觉得是非常差的。比如新的项目中,不需要做解密包操作了,(理论上libsdk.so库中应该不具备decode接口了),这样就会导致应用程序编译失败。undefine 'decode'。 因此我建议应用代码可以如下:(个人:应用层代码中声明时使用的自己版本的名字,逻辑代码使用的是自己版本的名字,但是此时的弱引用的别名使用的是库中的名字)

static __attribute__((weakref("pass"))) void mypass(void);
static __attribute__((weakref("verify"))) void myverify(void);
static __attribute__((weakref("decode"))) void mydecode(void);
static __attribute__((weakref("install"))) void myinstall(void);
static __attribute__((weakref("report"))) void myreport(void);
 
用户业务流程
...
if(mypass)
    mypass();
else
    printf("don't need pass\n");
...
if(myverify)
    myverify();
else
    printf("don't need verify\n");
...
if(mydecode)
    mydecode();
else
    printf("don't need decode\n");
...
if(myinstall)
    myinstall();
else
    printf("don't need install\n");
...
if(myreport)
    myreport();
else
    printf("don't need report\n");
...

    

    posted on 2022-05-04 15:10  朴素贝叶斯  阅读(399)  评论(0编辑  收藏  举报

    导航