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

转自:http://www.jb51.net/article/56924.htm

首先我表示很悲剧,在看《程序员的自我修养--链接、装载与库》之前我竟不知道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限制在只有本文件内可使用。

时间: 2024-10-02 01:27:46

浅谈C语言中的强符号、弱符号、强引用和弱引用【转】的相关文章

浅谈c语言中类型隐性转换的坑_C 语言

谨记:在C语言中,当两种不同类型之间运算时,低字节长度类型会向高自己长度类型转换,有符号会向无符号类型转换. 举例子如下: #include <stdio.h> void func(void) { int i = 1; unsigned char c1 = 1; signed char c2 = -1; if (c2 > i){ printf("\r\n -1 > 1"); } else{ printf("\r\n -1 <= 1");

浅谈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:(.d

浅谈c语言中转义字符的用法及注意事项_C 语言

c语言中的转义字符: \a 响铃符 \b 退格 \f 换页符 \n 换行符 \r 回车符(回到该行的首位置) \v 纵向制表符 \\ 反斜杠 \? 问号(?经vs10测试可以直接打印) \"(\') 双引号(单引号) \ooo 八进制数(ooo表示一个用8进制数表示出来的对应ANSII代码对应出字符,用此方法可以表示出所有ASCII字符.不过测试发现打不出%号,存疑!) \xhh 十六进制数(功能同八进制数,用hh表示一个十六进制数,如\x20表示空格)  注:使用转义字符的退格符,换行符,回车

浅谈Go语言中字符串和数组_Golang

go语言里边的字符串处理和PHP还有java 的处理是不一样的,首先申明字符串和修改字符串 复制代码 代码如下: package main import "fmt" var name string           //申明一个字符串 var emptyname string = "" //申明一个空字符串 func main() {  //申明多个字符串并且赋值  a, b, v := "hello", "word", &

浅谈C语言的字符串分割_C 语言

说起来很有意思,自认为对C语言理解得还是比较深刻的.但居然到今天才知道有个strtok函数,试用了一下突然感慨以前做了多少重复劳动.每次需要解析配置文件,每次需要分割字符串,居然都是自己去分割字符串,既累人又容易出错.感概技术学得不够全面啊!这里引用一段strtok用法: The strtok() function returns a pointer to the next "token" in str1, where str2 contains the delimiters that

浅谈C语言函数调用参数压栈的相关问题_C 语言

参数入栈的顺序 以前在面试中被人问到这样的问题,函数调用的时候,参数入栈的顺序是从左向右,还是从右向左.参数的入栈顺序主要看调用方式,一般来说,__cdecl 和__stdcall 都是参数从右到左入栈. 看下面的代码: #include <stdio.h> int test(int a, int b) { printf("address of a %x.\n", &a); printf("address of b %x.\n", &b)

用户体验设计:浅谈可用性测试中沟通的技巧

文章描述:如何快速解除用户防备?--浅谈可用性测试中沟通的技巧.   一般来说,在产品的设计和开发过程中,不同阶段会使用到不同的用户研究方法.比如,在产品正式发布之前,通常会进行可用性测试.可用性测试,是指让一群有代表性的用户尝试对产品进行典型操作,同时观察员和开发人员在一旁观察.聆听.记录.该产品可能是一个网站.软件,或其他任何产品,它可能已经做好,也可能尚未成型. 对于一个典型的可用行测试,我们可以:1. 通过观察用户在使用产品过程中出现的一些问题,发现产品的可用性问题2. 从测试参与者的表

浅谈网站SEO中避免pr高权重外链建设误区

做网站优化的站长需要经常要去发外链或者是交换友情链接,网站外链的数量和质量对网站的关键词排名有着非常重要的影响.我们去和别的网站交换友情链接,首先要考虑的就是对方网站是否和我们的网站门当户对.大家都喜欢高pr和高权重,认为权重高的网站自然能传递给我们网站不错的权重,从而提高我们网站的关键词排名.但事实上高pr和高权重并不一定会给我们网站带来好处,有时候还会影响搜索引擎对我们网站的印象. 网站优化中做外链最主要的就是把其他页面的权重传递给我们的网站,提高我们网站自身的权重,从而使关键词在搜索引擎中

浅谈SQL Server中的快照

原文:浅谈SQL Server中的快照 简介     数据库快照,正如其名称所示那样,是数据库在某一时间点的视图.是SQL Server在2005之后的版本引入的特性.快照的应用场景比较多,但快照设计最开始的目的是为了报表服务.比如我需要出2011的资产负债表,这需要数据保持在2011年12月31日零点时的状态,则利用快照可以实现这一点.快照还可以和镜像结合来达到读写分离的目的.下面我们来看什么是快照.   什么是快照     数据库快照是 SQL Server 数据库(源数据库)的只读静态视图