当前位置: 首页 > 编程笔记 >

浅谈C语言中的强符号、弱符号、强引用和弱引用

祁晟
2023-03-14
本文向大家介绍浅谈C语言中的强符号、弱符号、强引用和弱引用,包括了浅谈C语言中的强符号、弱符号、强引用和弱引用的使用技巧和注意事项,需要的朋友参考一下

首先我表示很悲剧,在看《程序员的自我修养--链接、装载与库》之前我竟不知道C有强符号、弱符号、强引用和弱引用。在看到3.5.5节弱符号和强符号时,我感觉有些困惑,所以写下此篇,希望能和同样感觉的朋友交流也希望高人指点。

  首先我们看一下书中关于它们的定义。

  引入场景:(1)文件A中定义并初始化变量i(int i = 1), 文件B中定义并初始化变量i(int i = 2)。编译链接A、B时会报错b.o:(.data+0x0): multiple definition of `i';a.o:(.data+0x0): multiple definition of `i'。(2)在文件C中定义并初始化两个变量i(int i = 1; int i = 2), 编译链接时会报错c.c:2:5: error: redefinition of ‘i'; c.c:1:5: note: previous definition of ‘i' was here。

  强符号:像场景中这样的符号定义被称为强符号,对于C/C++来说,编译器默认函数和初始化的全局变量为强符号。
  弱符号:接上文,为初始化的全局变量为弱符号。
  编译器关于强弱符号的规则有:(1)强符号不允许多次定义,但强弱可以共存;(2)强弱共存时,强覆盖弱;(3)都是弱符号时,选择占用空间最大的,如选择  double类型的而不选择int类型的。

  由以上定义所以有我之前没有想到的场景:
  代码a.c:

1 int i = 2;
  代码b.c:


#include<stdio.h>

int i; int main(int argc, char** argv) {       printf("i = %d\n", i);       return 0;      }

  编译文件a和b并链接,结果输出i为2而不是0。
  并且在同一个文件中定义但未初始化两个相同的变量不会报错,只有在使用变量时才会报错。
  对于GCC编译器来说,还允许使用__attribute__((weak))来将强符号定义为弱符号,所已有
  代码c.c


 #include<stdio.h>

  

  __attribute__((weak)) int i = 1;

  

  int main(int argc, char** argv)

  {

       printf("i = %d\n", i);

       return 0;   

  }

  结果i的输出仍未2而不是1。

  那么对于函数而言是不是也这样呢?先不看函数,而是先看由强弱符号而进一步引入的强弱引用。书中关于强弱引用的概述是对于强引用若未定义则链接时肯定会报错,而对于弱引用则不会报错,链接器默认其为0(这一点对于函数好理解,即函数符号所代表入口地址为0;对于变量就要注意了,既然是引用那自然就是地址了,所以同函数一样变量的地址为0而不是变量的值为0)。此时对于强弱引用是不是还没有什么明确的概念呢?到底什么是引用?引用和符号又是什么关系?这里我说一下我的理解(欢迎指正),在定义和声明处指定的函数名、变量名即为对应的符号,而在代码其他处调用函数或使用变量时,则把函说明和变量名看作引用,这样一来符号和引用在代码层面上其实就是一个东西,只是根据环境而叫法不同而已。那么强符号对应强引用,弱符号对应弱引用。

  有上面的强弱引用的特点可看出,当一个函数为弱引用时,不管这个函数有没有定义,链接时都不会报错,而且我们可以根据判断函数名是否为0来决定是否执行这个函数。这样一来,包含这些函数的库就可以以模块、插件的形式和我们的引用组合一起,方便使用和卸载,并且由于强符号可以覆盖弱符号和强弱符号与强弱引用的关系可知,我们自己定义函数可以覆盖库中的函数,多么美妙。

  先看根据条件判断是否执行函数:
  代码d.c


 #include<stdio.h>

 

void func()

{

     printf("func()#1\n");

}

  代码e.c


 #include<stdio.h>

  

 __attribute__((weak)) void func();

 

 int main(int argc, char** argv)

 {

      if (func)

          func();

      return 0;

 }


  编译d.c,cc -c d.c 输出d.o;编译e.c并链接d.o,cc d.o e.c -o e输出可执行文件e,运行e正常执行函数func。编译e.c但不链接d.o,此时并不会报错,只不过func不会执行,因为没有它的定义所以if(func)为假。
  再看函数覆盖:
  代码f.c


 #include<stdio.h>

 

 __attribute__((weak)) void func()

 {

      printf("func()#1\n");

 }

  代码g.c


 #include<stdio.h>

  

 void func()

 {

      printf("func()#2\n");

  }

  

 int main(int argc, char** argv)

 {

      func();

      return 0;

 }

 ~       

  编译链接,结构输出"func()#2"。

  以上可以说明函数和变量是保持一致的,其实对应变量也可以像使用函数那样先判断再使用,只不过不是判断变量的值而是变量的地址,如
  代码v1.c


int i = 2;

  代码v2.c


 #include<stdio.h>

  

 __attribute__((weak)) extern int i;

 

 int main(int argc, char** argv)

 {

      if (&i)

          printf("i = %d\n", i);

     return 0;

 }

 ~       

  编译并链接v1时,输出2;编译但不链接v1时无输出。这样做时要分清定义和声明的区别,__attribute__((weak)) int i 是定义变量并转换为弱符号,这样i是分配了空间的,而__attribute__((weak)) extern int i 则将原来定义的变量i由强符号转换为弱符号,导致使用i时不是强引用而是弱引用。不过虽然变量可以这么做但没有函数那样有意义。

  上面关于强弱引用仍旧使用的是GCC提供的__attribute__((weak)),而书中还提到了__attribute__((weakref)),后者貌似更能体现“引用”这一关键词。而我之所以使用前者来介绍强弱引用,是因为我对关于强弱符号与强弱引用对应关系的理解。关于__attribute__((weakref))的使用方法,这里介绍一种(两者都有不同的使用方法)。
  代码a.c


 #include<stdio.h>

 

 void bar()

 {

      printf("foo()\n");

 }

  代码b.c


 #include<stdio.h>

 

 static void foo() __attribute__((weakref("bar")));

 

  int main(int argc, char** argv)

 {

      if (foo)

         foo();

 

      return 0;

 }

  注意函数foo的static修饰符,没有的话会报错,这样将函数foo限制在只有本文件内可使用。

  好了,夜已深,写的有点凌乱,我也凌乱了。

 类似资料:
  • 介绍 弱引用缓存。对于一个给定的键,其映射的存在并不阻止垃圾回收器对该键的丢弃,这就使该键成为可终止的,被终止,然后被回收。丢弃某个键时,其条目从映射中有效地移除。该类使用了WeakHashMap做为其实现,缓存的清理依赖于JVM的垃圾回收。 使用 与TimedCache使用方法一致: WeakCache<String, String> weakCache = CacheUtil.newWeakC

  • 我在DotCMS中有多种语言。语言变量可以包含单引号或双引号。我在javascript中通过用引号包装这些变量来访问这些变量。 我怎样才能解决这个问题?

  • 问题内容: 分析这个问题后,我发现了有关在Linux 上动态加载()上下文中弱符号解析行为的一些信息。现在,我正在寻找管理该规范的规范。 让我们举个例子。假设有一个程序可以按此顺序动态加载库和。如果依赖于其他两个库(实际上在该示例中)和(实际上),则通常使用导出的符号可以满足中的弱符号链接。但是如果还依赖但不依赖,那么这些弱符号显然不会被联系起来。它好像inkages只能看从符号和及其所有的依赖关

  • 我在静态处理程序中使用弱引用来避免内存泄漏,然而,有时这个引用会被取消,我不明白为什么。 静态处理程序在存储库类中定义,该存储库类有一个在后台执行操作的方法,接收回调以在操作完成时通知调用方: 处理程序的代码如下所示: 出于某种原因,有时是空的,我试图理解为什么。 活动代码如下所示: 正如您所见,我正在为回调创建一个匿名类,但没有人持有对它的引用。 这就是弱引用无效的原因吗? 谢谢。

  • 本文向大家介绍简单的介绍一下强引用、软引用、弱引用、虚引用(虚引用与软引用和弱引用的区别、使用软引用能带来的好处)?相关面试题,主要包含被问及简单的介绍一下强引用、软引用、弱引用、虚引用(虚引用与软引用和弱引用的区别、使用软引用能带来的好处)?时的应答技巧和注意事项,需要的朋友参考一下 无论是通过引用计数法判断对象引用数量,还是通过可达性分析法判断对象的引用链是否可达,判定对象的存活都与“引用”有

  • 问题内容: 这些天来,我一直在使用Java反射和格式。我目前正在学习教学。 在JVM规范中,我发现了一个我不理解的术语: 符号引用 ,并且我有以下问题。 这是什么意思? 在哪里使用? 该指令在哪些情况下加载符号引用? Java中是否有与该操作对应的代码? 问题答案: 如果您引用给您带来麻烦的文档的确切内容,将很有帮助。既然您还没有,我将猜测一下ldc的文档中您可能引用的内容: 否则,如果运行时常量