首页 > 其他分享 >C语言中的强弱符号

C语言中的强弱符号

时间:2025-01-05 16:59:00浏览次数:3  
标签:__ 链接 20 定义 符号 int C语言 强弱

文章目录


在C语言中,强弱符号是与链接过程相关的重要概念,C++中不存在强弱符号,以下是对它们的详细讲解:

一、基本定义

  1. 强符号
    强符号通常是指在编译单元(一般是一个源文件,即 .c 文件)中定义的全局变量或者函数。例如,在一个 .c 文件中定义了如下全局变量:
int global_variable = 10;  // 这是一个强符号的全局变量定义

或者定义了这样的函数:

int my_function(int a) {
    return a + 1;
}  // 这是一个强符号的函数定义

强符号代表了一个确切的、具有唯一内存地址的实体,在链接阶段会参与到最终可执行文件的符号整合过程中。

  1. 弱符号
    弱符号相对特殊,它也是一种全局变量或者函数的表示形式,但允许在链接阶段被其他同名的强符号覆盖。在一些特定的应用场景中,比如在模块开发中,可能一个模块提供了某个功能的弱符号实现,而另一个模块可以用更强的实现来替换它。一般未初始化的普通全局变量 int g_val;

在常见的GCC编译器中,可以使用 __attribute__((weak)) 语法来声明一个弱符号。例如,声明一个弱符号的全局变量可以这样做:

int __attribute__((weak)) weak_variable = 5;  // 这是一个弱符号的全局变量声明及定义

对于函数,同样可以定义为弱符号形式:

int __attribute__((weak)) weak_function(int a) {
    return a;
}

二、链接过程中的行为

  1. 同名强符号冲突
    当链接器在处理多个编译单元时,如果发现存在同名的强符号(比如两个不同的 .c 文件都定义了一个名为 same_name_variable 的全局变量且都是强符号形式),那么链接器会报错,提示符号重复定义(因为强符号都期望有唯一确定的定义进入最终可执行文件)。例如:
    文件 file1.c 中有:
int same_name_variable = 10;

文件 file2.c 中有:

int same_name_variable = 20;

在链接这两个文件生成可执行文件时,链接器会指出 same_name_variable 重复定义的错误,无法继续生成可执行文件。

  1. 强符号与弱符号并存
    如果一个符号既有强符号定义又有弱符号定义,在链接阶段,最终会使用强符号。例如,文件 file1.c 定义了强符号的全局变量:
int combined_variable = 10;

而文件 file2.c 定义了同名的弱符号全局变量:

int __attribute__((weak)) combined_variable = 20;

在链接这两个文件后,最终可执行文件中使用的是 file1.c 中定义的强符号对应的 combined_variable 的值(也就是 10),弱符号的定义被忽略了。

  1. 多个弱符号情况
    当存在多个同名的弱符号时,链接器会选择其中一个(不同编译器或者不同链接器的具体选择策略可能略有差异,但一般是按照某种顺序选取,比如先遇到的那个等)一般会选择内存占用交大的那个,并且不会报错。例如,文件 file1.c 中有:
int __attribute__((weak)) multi_weak_variable = 10;

文件 file2.c 中有:

int __attribute__((weak)) multi_weak_variable = 20;

链接器可能会选择 file1.c 中定义的那个弱符号(假设按照遇到顺序选取等情况),并将其纳入最终可执行文件中,当然这种情况需要开发者根据具体需求谨慎使用,避免出现不符合预期的结果。

三、应用场景

  1. 库函数的可选实现
    在一些库的开发中,可以将某些函数定义为弱符号。比如一个设备驱动库,对于某个特定功能的函数,在不同的硬件平台上可能有不同的实现方式,开发者可以将基础的、通用的实现定义为弱符号,然后当针对具体硬件平台编译时,如果有更适配的强符号函数实现(比如针对该硬件专门优化过的函数),就可以覆盖弱符号对应的函数,这样能增强库的灵活性和可扩展性。

  2. 插件式开发
    在插件式的软件架构中,主程序可以定义一些弱符号的函数或者变量,插件模块可以根据自身需求提供强符号的对应实现来覆盖主程序中的弱符号,从而实现特定的功能扩展,同时又保证了在没有插件覆盖时主程序也能有基本的、默认的运行状态。

总之,强弱符号在C语言中为程序的模块化开发、不同实现的灵活替换等方面提供了有效的机制,合理运用它们可以提升代码的可维护性和可扩展性。

四、强弱符号示例1

a.c


int g_data1;

void fun()
{
	g_data1 = 20;
}

main.c

#include<stdio.h>
extern void fun();

int g_data1=1;
int main()
{
	fun();
	printf("%d\n" , g_data1);
	return 0;
}

打印结果 20
在这里插入图片描述
分析如下
1、编译,编译阶段a.c fun()函数,g_data1 = 20;//往g_data1所在的内存空间写入数据20 , 写多大呢,因为编译阶段能看到本文件内g_data1为int 型 , 所以写四个字节;
2、链接阶段,因为a.c 里 g_data1只有声明,没有定义,所以是弱符号,普通的、全局的、未初始化的变量,在编译阶段生成符号是放在一个特定的区域,在linux里是COM块 , 只有在链接后,进行强弱符号选择后,才会把符号放进.bss或者.data段 , 若符号在链接阶段会重新进行强弱符号的选择,最终选择强符号,也就是main.c里的g_data1 , 相当于往main.c的g_data1里写进20 ;
3、main.c编译阶段printf打印的就是强符号,所以打印出来就是20

五、稍有难度示例2

main.c

#include<stdio.h>
extern void fun();

char  gdata1=10;
char  gdata2 = 10;
int main()
{
	fun();
	printf("%d , %d\n" , gdata1 , gdata2);
	return 0;
}


a.c


int gdata1;

void fun()
{
	gdata1 = 20;
}

在这里插入图片描述
分析如下:
1、编译阶段,a.c gdata1为弱符号,函数内gdata1 = 20;编译为往gdata1所在的内存空间写入20,因为编译阶段智能看到本文件内的符号,此时gdata1为Int 型 , 所以写入四个字节;
2、main.c在链接时,开始强弱符号选择,选择了强符号,强符号为char类型的,那就糟糕了,因为在编译阶段就已经确定了指令了,往gdata1所在内存写入四个字节,写入数据20,如下图,从gdata1开始往后四个字节写入20也解释0x 00 00 00 14 , 把gdata2的数据也给覆盖了,原本是10 , 0a,覆盖后变成了0 , 所以打印出来就是 20 ,0

在这里插入图片描述
在这里插入图片描述

标签:__,链接,20,定义,符号,int,C语言,强弱
From: https://blog.csdn.net/doubleintfloat/article/details/144894129

相关文章

  • 【C语言编程】地址与指针:理解与应用
    在C语言编程中,理解地址和指针的概念至关重要。地址是内存中可以唯一标识内存单元的数字,而指针则是存储这些地址的变量。通过指针,程序员可以直接访问和操作内存,这是C语言强大功能的基础。地址的概念在C语言中,每个变量都存储在内存中的一个特定位置,这个位置就是变量的地址。例如,一......
  • 【AI学习笔记5】用C语言实现一个最简单的MLP网络 A simple MLP Neural network in C
    用C语言实现一个最简单的MLP网络AsimpleMLPNeural NetworkinClanguage 从图像中识别英文字母【1】从图像中识别多个不同的数字,属于多分类问题;每个图像是5*5的像素矩阵,分别包含1-5五个字母数字; 网络结构:一个隐藏层的MLP网络;       每个图像是5x5个......
  • OpenWrt 系统UCI详解(Lua、C语言调用uci接口实例)
    1UCI简介“uci"是"Unified Configuration Interface”(统一配置界面)的缩写,用于OpenWrt整个系统的配置集中化。很多后台服务有自己的配置文件,并且配置文件格式都不相同,OpenWrt系统中需要开启各种服务,为了解决配置不兼容的问题,统一采用uci格式的配置文件。当然统一的前提......
  • C语言操作XML文件的原理与实践
    摘要XML(可扩展标记语言)因其灵活性和标准化特性,被广泛应用于数据交换、配置文件、Web服务等领域。C语言作为一种高效、底层的编程语言,在处理XML数据方面也有广泛的应用。本文将深入探讨C语言操作XML的技术和方法,帮助读者掌握C语言处理XML的技巧。主要内容包括XML的基本概念......
  • C/C++调试---调试符号与调试器
    调试符号与调试器调试符号调试符号由编译器生成,与相关的机器代码、全局数据对象等一同产生。链接器会收集并组织这些符号,将他们写入可执行文件的调试部分,或存储到一个单独文件中。概览全局函数和变量源文件和行信息为了优化程序性能,编译器可能会对源代码进行位移,情......
  • 【C语言】数组——二分查找
    题1704.二分查找【简单】intsearch(int*nums,intnumsSize,inttarget){intleft=0,right=numsSize-1;intmid=(left+right)/2;intresult=-1;while(left<=right){if(nums[mid]==target){r......
  • C语言:结构体
    C语言已经提供了内置类型,如:char、short、int、long、float、double等,但在处理一些问题时只有这些内置类型还是不够的,假设我想描述学生,这时单一的内置类型是不行的。描述一个学生需要名字、年龄、学号、身高、体重等。C语言为了解决这个问题,增加了结构体这种自定义的数据类型,让......
  • STLG_01_09_程序设计C语言 - 指针
        C语言中的指针是一个非常重要的概念,它允许程序直接访问和操作内存地址。理解指针对于掌握C语言编程至关重要。1.指针的基本概念指针:指针是一个变量,它存储的是另一个变量的内存地址。指针变量:指针变量专门用来存储内存地址。2.指针的声明与初始化2.1指针的声......
  • 【C语言程序设计——函数】编写函数求解累加和(头歌实践教学平台习题)【合集】
    目录......
  • C语言初阶习题【20】扫雷游戏
    1.用C语言实现扫雷游戏本博客和三子棋游戏比较大的区别是,三子棋游戏是写完了再总结的,本博客是边代码实现边编辑博客,所以本博客会比较详细的po出每一步骤,在每实现一个小功能的时候我们都先验证下效果,再继续下一步。2.思路总体的思路和三子棋游戏是一样的,我们把游戏实现部......