C语言中的强符号与弱符号

参考《程序员的自我修养》
参考 C语言中的强符号与弱符号

符号重复定义

main.c

int a = 100;

int main(){

    printf("%d\n",a);
    return 0;
}

other.c

int a = 10;

编译

gcc main.c other.c

编译结果

  • clang

ld: 1 duplicate symbol for architecture x86_64
clang: error: linker command failed with exit code 1
  • gcc

multiple definition of `a' 

比如:我们在目标文件A和目标文件B都定义了一个全局整形变量global,并将它们都初始化,那么链接器将A和B进行链接时会报错!

这种符号的定义可以被称为强符号(Strong Symbol)
函数,初始化了的全局变量为强符号
未初始化的全局变量为弱符号

注意
下面的__attribute__((weak))是gcc提供的将强符号转为弱符号的关键字

main.c

extern int ext;  
int weak1;  
int strong = 1;  
int __attribute__((weak)) weak2 = 2;  
  
int main()  
{  
          printf("%d,%d,%d\n", weak1,strong,weak2);
        return 0;  
}  

test.c

int weak1 = 100;

//int strong = 100; //multiple definition of `strong' 

int weak2 = 200;  

编译
gcc mainc test.c

结果
100,100,200

这里有人会将强符号理解为extern,因为在mainc和test.c两个模块中共享全局变量,你可以打印地址(地址是一样的)。
但两者是本质不同的
extern 本质就是声明变量,告诉linker去其他模块找定义。
而强符号和弱符号可是实打实的定义。虽然我们说弱符号没有初始化。

关于定义与声明的区别,请看这篇文章c语言的定义与声明

说实话,我也是看了《程序员的自我修养》之后,才知道有强符号和弱符号的概念,而且在mac上试了一下,不行,只能在gun linux上玩。看了各个厂商在这方面的实现出入还很大!

结论
  • 同名的强符号只能有一个,否则编译器报"重复定义"错误

如果去掉test.c中strong的定义,就会报"重复定义"错误。

我的理解

首先,从上面的例子可以看到,两个模块共享变量。但是又和extern不同,extern就是声明,完全没有分配内存。但是这里可是定义,照样共享内存了!

例子1

weak.c

void __attribute__((weak)) f();  
int main(void)  
{  
        if (f)  
        f();  
        return 0;  
}  

编译

gcc weak.c

结果
通过了,并且还能运行!很奇怪吧

例子2

增加一个f.c

#include <stdio.h>  
void f(void)  
{  
        printf("hello from f\n");  
}  

编译

gcc weak.c f.c

结果
hello from f

例子3

修改weak.c

#include <stdio.h>  
void __attribute__((weak)) f()  
{  
        printf("hello from weak\n");  
}  
int main(void)  
{  
        f();  
        return 0;  
}  

编译

gcc weak.c

结果
hello from weak

编译

gcc weak.c f.c

结果
hello from f

注意
编译方式不同,结果不同

结论

根据上面三个例子,得到结论:

  • 允许一个强符号和多个弱符号,但定义会选择强符号的。

例1,只有弱符号f,编译通过!没有报reference f not defined!
例2,3 f.c是强符号,结果执行结果用的是f.c中的f定义

最后一个例子4

main.c

#include <stdio.h>  
#include <stdlib.h>  
  
extern int fun(void);  
  
int global_var1 = 0xff00ff00;       // 强符号  
  
int main(int argc, const char *argv[])  
{  

    printf("in main.c: &global_var1 = %p", &global_var1);  
    printf(" global_var1 = %x\n", global_var1);  
    printf("sizeof(global_var1) = %d\n", sizeof(global_var1));  
/////////////////////////////////////////////////////////////////////  
    fun();  
  
    printf("global_var1 = %x\n", global_var1);  
    printf("global_var2 = %x\n", global_var2);  
  
    return 0;  
}  

test.c

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
  
double global_var1;  
  
int fun(void)  
{  
    printf("in test.c: &global_var1 = %p", &global_var1);  
    printf(" global_var1 = %x\n", global_var1);  
    printf("sizeof(global_var1) = %d\n", sizeof(global_var1));  
  
    memset(&global_var1, 0, sizeof(global_var1));  
  
    return 0;  
}  

  • main.c 和 test.c都有一个global_var1,在main.c中的为强符号,在test.c中的为弱符号.

  • main.c中的global_var1和test.c中的global_var1引用的时同一块内存区域,只是在两个文件中代表的意义不同

  • 在main.c中代表一个int型变量,在test.c中代表一个double型变量,它们的起始地址相同,但占用内存空间是不同的, 在main.c中占用4个字节,在test.c中占用8个字节,这点从上图的两个sizeof输出结果中可以得到验证

结论
  • 当有多个弱符号相同时,链接器选择占用内存空间最大的那个


Sike
272 声望18 粉丝