C语⾔中的强符号与弱符号(关于变量声明与定义的深⼊讨
论)
看到⼀篇介绍C语⾔强符号与弱符号的⽂章⾮常好,转载过来加深印象。
==============================================================================
⼀、概述
在C语⾔中,函数和初始化的全局变量(包括初始化为0)是强符号,未初始化的全局变量是弱符号。
对于它们,下列三条规则使⽤:
①同名的强符号只能有⼀个,否则编译器报"重复定义"错误。
②允许⼀个强符号和多个弱符号,但定义会选择强符号的。
③当有多个弱符号相同时,链接器选择占⽤内存空间最⼤的那个。
⼆、哪些符号是弱符号?
我们经常在编程中碰到⼀种情况叫符号重复定义。多个⽬标⽂件中含有相同名字全局符号的定义,那么这些⽬标⽂件链接的时候将会出现符号重复定义的错误。⽐如我们在⽬标⽂件A和⽬标⽂件B都定义了⼀个全局整形变量global,并将它们都初始化,那么链接器将A和B进⾏链接时会报错:
1 b.o:(.data+0x0): multiple definition of `global'
2 a.o:(.data+0x0): first defined here
这种符号的定义可以被称为 强符号(Strong Symbol)。有些符号的定义可以被称为 弱符号(Weak Symbol)。 对于C语⾔来说,编译器默认函数和初始化了的全局变量为强符号,未初始化的全局变量为弱符号(C++并没有将未初始化的全局符号视为弱符号)。我们也可以通过GCC的"__attribute__((weak))"来定义任何⼀个强符号为弱符号。 注意,强符号和弱符号都是针对定义来说的,不是针对符号的引⽤。⽐如我们有下⾯这段程序:
extern int ext;
int weak;
int strong = 1;
__attribute__((weak)) weak2 = 2;
int main()
{
return 0;
}
上⾯这段程序中,"weak"和"weak2"是弱符号,"strong"和"main"是强符号,⽽"ext"既⾮强符号也⾮弱符号,因为它是⼀个外部变量的引⽤。
下⾯⼀段话摘⾃ :
In , a weak symbol is a symbol definition in an or that may be overridden by other symbol definitions. Its value will be zero if no definition is found by the loader.
换句话说,就是我们可以定义⼀个符号,⽽该符号在链接时可以不解析。
让我们再看⼀些例⼦:
$ cat err.c
int main(void)
{
f();
return 0;
}
很明显,不能编译通过,因为'undefined reference' :
c语言和c++区别$ gcc err.c
/tmp/ccYx7WNg.o: In function `main':
err.c:(.text+0x12): undefined reference to `f'
collect2: ld returned 1 exit status
那么,如果将符号f声明成弱符号,会怎么呢?
$ cat weak.c
void __attribute__((weak)) f();
int main(void)
{
if (f)
f();
return 0;
}
$ gcc weak.c
居然编译通过了,甚⾄成功执⾏!让我们看看为什么?
⾸先声明了⼀个符号f(),属性为weak,但并不定义它,这样,链接器会将此未定义的weak symbol赋值为0,也就是说f()并没有真正被调⽤,试试看,去掉if条件,肯定core dump!
我们可以通过nm来查看符号:
$ nm a.out
...
w f
08048324 T main
...
如果我们在另⼀个⽂件中定义函数f,与week.c⼀起编译链接,那么函数f就会正确的被调⽤!
$ cat f.c
#include <stdio.h>
void f(void)
{
printf("hello from f\n");
}
$ gcc -c weak.c f.c
$ gcc -o weak weak.o f.o
$ ./weak
hello from f
$ nm weak.o
w f
00000000 T main
$ nm f.o
00000000 T f
U puts
$ nm weak
...
08048384 T f
08048354 T main
U puts@@GLIBC_2.0
...
我们甚⾄可以定义强符号来override弱符号:
$ cat orig.c
#include <stdio.h>
void __attribute__((weak)) f()
{
printf("original f..\n");
}
int main(void)
{
f();
return 0;
}
$ gcc orig.c
$ ./a.out
original f..
$ cat ovrd.c
#include <stdio.h>
void f(void)
{
printf("overridden f!\n");
}
$ gcc -c orig.c ovrd.c
$ gcc -o ovrd orig.o ovrd.o
$ ./ovrd
overridden f!
00000014 T main
U puts
$ nm ovrd.o
00000000 T f
U puts
$ nm ovrd
...
0804838c T f
08048368 T main
U puts@@GLIBC_2.0
...
或者如下:
$ cat orig-obj.c
#include <stdio.h>
int __attribute__((weak)) x = 1;
int __attribute__((weak)) y = 1;
int main(void)
{
printf("x = %d, y = %d\n", x, y); return 0;
}
$ gcc orig-obj.c
$ ./a.out
x = 1, y = 1
$ cat ovrd-obj.c
int x = 2;
void f(void)
{
}
$ gcc -c orig-obj.c ovrd-obj.c
$ gcc -o ovrd-obj orig-obj.o ovrd-obj.o $ ./ovrd-obj
x = 2, y = 1
U printf
00000000 V x
00000004 V y
$ nm ovrd-obj.o
00000000 T f
00000000 D x
$ nm ovrd-obj
...
08048394 T f
08048354 T main
U printf@@GLIBC_2.0
080495c8 D x
080495c4 V y
...
那么当出现multiple symbols时,会如何呢?
$ cat mul.c
int main(void)
{
f();
return 0;
}
$ cat s1.c
#include <stdio.h>
void f(void)
{
printf("1st strong f from %s\n", __FILE__); }
$ cat s2.c
#include <stdio.h>
void f(void)
{
printf("2nd strong f from %s\n", __FILE__); }
$ cat w1.c
#include <stdio.h>
void __attribute__((weak)) f(void)
{
printf("1st weak f from %s\n", __FILE__); }
$ cat w2.c
#include <stdio.h>
void __attribute__((weak)) f(void)
{
printf("2nd weak f from %s\n", __FILE__); }
$ gcc -c mul.c s1.c s2.c w1.c w2.c
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论