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

强符号、弱符号、强引用和弱引用

符号的概念

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.cb.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技术站

(0)
上一篇 2023年5月24日
下一篇 2023年5月24日

相关文章

  • 如何判断一个数是否为2的幂次方?若是,并判断出来是多少次方?

    判断一个数是否为2的幂次方: 一个数如果是2的幂次方,那么它的二进制表示中只有最高位是1,其他各位都是0。比如2的1次方是2,写成二进制就是10;2的2次方是4,写成二进制是100;2的3次方是8,写成二进制是1000。 根据这个规律,我们可以用位运算来判断一个数是否为2的幂次方,具体方法如下: 首先判断这个数是否大于0,如果为0则不是2的幂次方; 然后判断…

    C 2023年5月23日
    00
  • TIOBE编程语言排行榜前20的语言入门书籍推荐

    TIOBE编程语言排行榜前20的语言入门书籍推荐攻略 TIOBE编程语言排行榜是一个广受欢迎的编程语言排名网站,每月发布最新的排行榜,展示当月最受欢迎的编程语言。本攻略将会介绍排行榜前20的编程语言,并针对每种编程语言推荐一本入门书籍。 1. Java Java是一个广泛应用的编程语言,每年都会有大量的Java招聘。入门者可以从下列书籍开始学习Java: 《…

    C 2023年5月23日
    00
  • 关于C语言中参数的传值问题

    关于C语言中参数的传值问题 在C语言中参数的传递方式有两种:传值(Call by Value)和传址(Call by Reference)。 传值(Call by Value) 对于传值方式,函数只能访问传递进来的参数的值,无法修改传递进来的参数本身。传递的是参数的复制品而不是原始参数。 以下是传值方式的示例代码: #include <stdio.h&…

    C 2023年5月23日
    00
  • Win11提示rundll32.exe应用程序错误怎么办 rundll32.exe错误提示修复教程

    Win11提示rundll32.exe应用程序错误怎么办 当用户在Win11操作系统中打开某些应用程序时,可能会遭遇到rundll32.exe应用程序错误。这个问题可能会严重干扰一个人的日常计算机使用。然而,这个问题并不是无法解决的。接下来就让我们一步步来解决这个问题。 什么是rundll32.exe? rundll32.exe是一个Windows系统进程,…

    C 2023年5月23日
    00
  • PHP5.4中json_encode中文转码的变化小结

    下面给您简单介绍一下“PHP5.4中json_encode中文转码的变化小结”这个主题的攻略。 什么是json_encode()? json_encode() 是PHP语言提供的一个函数,它用于将PHP数据转换为json格式。 PHP5.4中json_encode中文转码的变化 在 PHP 5.4 版本之前,json_encode() 函数对于非 ASCII…

    C 2023年5月23日
    00
  • C++ 实现2048游戏示例

    C++ 实现2048游戏示例攻略 1. 简介 2048是一个深受欢迎的数字游戏,我们可以使用C++语言实现这个游戏。在本文中,我们将深入了解如何用C++实现一个完整的2048游戏。 2. 程序设计思路 我们可以使用C++语言中的面向对象思想来实现2048游戏。对于每个数字方块,可以定义一个方块类,类中包含了每个方块所需要的数据和方法。每个方块都有自己的数字数…

    C 2023年5月23日
    00
  • C/C++ 编译器优化介绍

    C/C++ 编译器优化介绍 C/C++ 编译器通过优化可以让代码运行更快、更高效,提升程序的性能和响应速度。本文将介绍常用的 C/C++ 编译器优化技术,以及对应的编译器选项和示例说明。 基本编译器优化 优化等级 编译器一般提供多个不同的优化等级,包括 “-O0” 到 “-O3″ 等级。其中,”-O0″ 表示不进行任何优化,而 “-O3” 表示最高级别的优化…

    C 2023年5月22日
    00
  • VsCode的jsconfig配置文件说明详解

    下面是关于VsCode的jsconfig配置文件说明详解的完整攻略。 什么是 jsconfig.json? jsconfig.json 是一个用于在 VS Code 中指定 JavaScript 项目根目录的配置文件。 在 jsconfig.json 文件中,我们可以通过配置路径映射、JS 声明、解析模块、编译选项等,来使 VS Code 更好地为 Java…

    C 2023年5月23日
    00
合作推广
合作推广
分享本页
返回顶部