强符号、弱符号、强引用和弱引用
符号的概念
在C语言中,符号通常指的是变量、函数或者地址的名称。当我们使用这些名字的时候,编译器会将其转换成对应的地址或者值。但是,有些情况下我们并不希望这些名字被编译器处理,而是需要自己处理这些名字所代表的地址或者值,这就需要了解符号的相关概念。
符号的属性
在C语言中,符号有四个属性:强符号、弱符号、强引用和弱引用。这四个属性是程序运行过程中的重要概念,深入理解它们有助于我们更好地理解链接和符号的工作原理。
强符号
强符号是指在链接过程中会被优先选中的符号。当程序中存在多个同名符号时,链接器会选择这些符号中属性最强的一个作为最终的符号。在C语言中,全局变量和函数都是强符号,它们的地址无法被改变,链接器会优先使用它们。
例如,我们声明了一个全局变量和一个函数:
int global_var = 0;
void func() {
global_var = 1;
}
编译后得到的汇编代码如下:
_global_var:
.long 0
_func:
push ebp
mov ebp, esp
mov dword ptr [_global_var], 1
pop ebp
ret
可以看到,变量global_var
和函数func
都被编译器赋予了标号,这些标号成为了强符号。
弱符号
弱符号是指在链接过程中,如果存在多个同名符号,则链接器会选择属性最弱的符号作为最终的符号。在C语言中,静态变量和函数都是弱符号,它们的地址可以被改变。
例如,我们声明了一个静态变量和一个静态函数:
static int static_var = 0;
static void static_func() {
static_var = 1;
}
编译后得到的汇编代码如下:
_static_var:
.long 0
_static_func:
push ebp
mov ebp, esp
mov dword ptr [_static_var], 1
pop ebp
ret
可以看到,静态变量static_var
和静态函数static_func
都被编译器赋予了标号,这些标号成为了弱符号。
强引用
强引用是指在程序中直接引用一个符号,链接器会将这个符号和它在程序中的位置联系起来。在C语言中,对全局变量和函数的直接引用都是强引用。
例如,我们在代码中使用了全局变量和函数:
#include <stdio.h>
int global_var;
void func();
int main() {
global_var = 1;
func();
printf("global_var: %d\n", global_var);
return 0;
}
编译后得到的汇编代码如下:
_main:
push ebp
mov ebp, esp
mov dword ptr [_global_var], 1
call _func
push dword ptr [_global_var]
push OFFSET FLAT:.LC0
call _printf
add esp, 8
xor eax, eax
pop ebp
ret
可以看到,在代码中对全局变量global_var
和函数func
的引用都是强引用。
弱引用
弱引用是指在程序中间接引用一个符号,链接器不会将这个符号联系起来。在C语言中,对静态变量和函数的间接引用、对全局变量和函数的非直接引用都是弱引用。
例如,我们声明了一个静态变量和一个静态函数,通过另一个函数间接引用它们:
#include <stdio.h>
static int static_var = 0;
static void static_func() {
static_var = 1;
}
void call_static_func() {
static_func();
}
int main() {
call_static_func();
printf("static_var: %d\n", static_var);
return 0;
}
编译后得到的汇编代码如下:
_static_var:
.long 0
_static_func:
push ebp
mov ebp, esp
mov dword ptr [_static_var], 1
pop ebp
ret
_call_static_func:
push ebp
mov ebp, esp
call _static_func
pop ebp
ret
_main:
push ebp
mov ebp, esp
call _call_static_func
push dword ptr [_static_var]
push OFFSET FLAT:.LC0
call _printf
add esp, 8
xor eax, eax
pop ebp
ret
可以看到,在主函数中对静态变量static_var
和静态函数static_func
的引用都是弱引用,而在call_static_func
函数中对静态函数static_func
的引用是强引用。
总结
符号的属性对C语言程序链接和运行有着重要的影响。强符号和弱符号主要涉及到程序在链接阶段对同名符号的选择,并且强符号的地址无法被改变,而弱符号的地址是可以被改变的。强引用和弱引用主要涉及到程序中对符号的直接引用和间接引用。深入理解这些概念有助于我们更好地理解链接和符号的工作原理。
示例1
我们假设有两个文件a.c
和b.c
,其中a.c
定义了一个全局变量,b.c
中包含了一个函数,这个函数对全局变量进行了修改。我们来看一下这个程序的运行结果,及其背后的原因。
a.c
#include <stdio.h>
int global_var = 0;
int main() {
printf("before modify: %d\n", global_var);
modify_global_var();
printf("after modify: %d\n", global_var);
return 0;
}
b.c
#include <stdio.h>
extern int global_var;
void modify_global_var() {
global_var = 1;
}
$ gcc a.c b.c
$ ./a.out
before modify: 0
after modify: 1
可以发现,程序运行后,全局变量global_var
的值从0变为了1。这是因为在链接阶段,链接器优先选择了a.c
中定义的全局变量,并将这个全局变量与b.c
中的函数进行了连接。因此,在b.c
中修改全局变量的值,实际上是修改了a.c
中定义的全局变量。
示例2
我们再来研究一个例子,这个例子中声明了一个静态变量和一个静态函数,这个函数对这个静态变量进行了修改。我们来看一下这个程序的运行结果,以及背后的原因。
#include <stdio.h>
static int static_var = 0;
static void static_func() {
static_var = 1;
}
void call_static_func() {
static_func();
}
int main() {
call_static_func();
printf("static_var: %d\n", static_var);
return 0;
}
$ gcc main.c
$ ./a.out
static_var: 0
可以发现,程序运行结束后,静态变量static_var
的值还是0,没有被函数修改。这是因为对静态变量和静态函数的引用都是弱引用,静态变量的地址可以被改变,而静态函数的优先级比全局变量低,因此链接器不会将静态函数与静态变量进行连接。因此,函数对静态变量的修改并没有生效。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:浅谈C语言中的强符号、弱符号、强引用和弱引用 - Python技术站