嵌入式C语言:什么是共用体?

在嵌入式C语言编程中,共用体(Union)是一种特殊的数据结构,它允许在相同的内存位置存储不同类型的数据。意味着共用体中的所有成员共享同一块内存区域,因此,在任何给定时间,共用体只能有效地存储其成员之一的值。当存储一个新值时,它会覆盖共用体中之前存储的任何数据。

一、共用体的定义

共用体通过关键字 union 来定义,后面跟着共用体的名称和一对花括号,花括号内是共用体的成员列表。每个成员的定义与结构体中的成员定义类似。

union MyUnion {
    int i;
    float f;
    char str[10];
};

union MyUnion 可以存储一个 int、一个 float 或一个9字符的字符串(因为字符串的最后一个位置需要存储空字符 \0 以表示字符串的结束)。但是,由于这些成员共享内存,所以不能同时存储所有这些值并期望它们都有意义。

二、共用体的内存布局

2.1. 成员存储位置

共用体的所有成员都存储在同一块连续的内存空间中,它们的起始地址相同。例如:

union Example {
    int num;
    char ch;
    float f;
};

在这个共用体中,numchf都从同一块内存空间的起始位置开始存储。

2.2. 内存大小确定

共用体的内存大小等于其最大成员的大小。这是为了确保能够容纳所有成员中占用空间最大的数据类型。比如在上述例子中,如果int类型占 4 个字节,char类型占 1 个字节,float类型占 4 个字节,那么union Example的大小就是 4 个字节。因为要保证这块内存空间既能存储int类型的数据,也能存储float类型的数据。

2.3. 内存覆盖情况

由于所有成员共享同一块内存,当给其中一个成员赋值时,会覆盖其他成员在内存中的值。例如: 

union Example example;
example.num = 10;
// 此时example.ch和example.f的值是不确定的,因为它们在内存中的值被example.num的赋值操作覆盖了

example.f = 3.14;
// 此时example.num和example.ch的值又被example.f的赋值操作覆盖了

2.4. 特殊情况

如果共用体中有数组成员,那么数组的大小将作为确定共用体内存大小的关键因素。比如:

union AnotherExample {
    int num;
    char ch;
    float f;
    char str[10];
};

在这种情况下,如果char str[10]的大小(10 个字节)大于intcharfloat类型的大小,那么union AnotherExample的大小就是 10 个字节。并且在使用共用体时,对str数组的操作会影响到其他成员在内存中的值,反之亦然。

三、共用体成员访问

可以通过共用体变量来访问其成员,例如:

#include <stdio.h>

// 定义共用体
union Data {
    int i;
    float f;
    char c;
};

int main() {
    union Data data;

    // 给整型成员赋值
    data.i = 10;
    printf("data.i = %d\n", data.i);

    // 给浮点型成员赋值,会覆盖之前整型成员的值
    data.f = 3.14f;
    printf("data.f = %f\n", data.f);

    // 给字符型成员赋值,会覆盖之前浮点型成员的值
    data.c = 'A';
    printf("data.c = %c\n", data.c);

    // 再次访问整型成员,由于其值已被覆盖,结果是未定义的
    printf("data.i (after modification) = %d\n", data.i);

    return 0;
}

 

定义了一个共用体Data,它包含intfloatchar三种不同类型的成员。通过共用体变量data,可以分别给不同的成员赋值并访问。需要注意的是,每次给一个成员赋值时,都会覆盖其他成员在内存中的值。所以在访问成员时,要确保访问的是最近一次赋值的成员,否则可能得到未定义的结果。例如,在给data.c赋值后再访问data.i,此时data.i的值是未定义的。 

四、共用体特点与用途

4.1. 特点

内存共享:共用体所有成员共享同一块内存区域,其内存大小等于最大成员的大小。这使得在同一时刻,只有一个成员的值是有效的,对一个成员赋值会覆盖其他成员在内存中的值 。

4.2. 用途

  • 节省内存:共用体(Union)在嵌入式系统中非常有用,特别是在需要节省内存的场景中。当程序需要在不同时刻存储不同类型的数据,但不需要同时存储这些数据时,共用体可以共享同一块内存区域,从而节省空间。
  • 数据类型转换:共用体还可以用于不同类型数据之间的转换。例如,在处理网络通信或文件格式时,可能需要将整数、浮点数或字符串等数据类型相互转换。共用体提供了一种方便的方式来实现这些转换。
  • 访问硬件寄存器:在嵌入式系统中,硬件寄存器的访问是常见的操作。共用体可以用于映射这些寄存器,并允许以不同的方式(如字节、半字、字等)访问它们。这对于读取和设置寄存器的特定位或位域非常有用。

五、使用场景

5.1.通信协议数据解析

在通信协议中,根据不同的命令类型会接收不同格式的数据,但同一时刻只会接收一种类型的数据。例如,在一个简单的传感器网络通信协议中,可能有温度传感器数据、湿度传感器数据和光照传感器数据等不同类型的数据包。可以定义一个共用体来存储接收到的数据:

union SensorData {
    int temperature;
    float humidity;
    unsigned int light;
};

这样,在接收到不同类型的数据时,可以通过共用体来灵活存储和解析,节省内存空间。

5.2. 硬件寄存器访问

在嵌入式系统中,经常需要访问硬件寄存器,这些寄存器可能包含不同类型的数据。例如,一个控制外设的寄存器,既可以以字节的形式访问其某个位域,也可以以整数的形式访问整个寄存器的值。可以使用共用体来方便地进行访问:

union RegisterData {
    unsigned char byte_data;
    unsigned int int_data;
};

通过这个共用体,可以根据需要以不同的方式操作同一个寄存器。

5.3. 数据类型转换

可以利用共用体来实现不同数据类型之间的转换。例如,将一个整数以字节的形式存储在共用体中,然后以字符数组的形式访问这些字节,从而实现整数到字节数组的转换。这在数据的传输和存储格式转换场景中很实用。以下是一个示例: 

union DataConversion {
    int int_value;
    char byte_array[4];
};

通过这个共用体,可以方便地将整数转换为字节数组,或者将字节数组转换为整数。

5.4. 节省内存空间

当程序需要在不同时刻使用不同类型的数据,但不需要同时存储这些数据时,共用体可以节省内存空间。例如,在一个资源受限的嵌入式设备中,可能需要记录不同类型的传感器数据,但在某一时刻只需要存储一种传感器的数据。可以使用共用体来存储这些数据:

union SensorValue {
    int temperature;
    float pressure;
    long humidity;
};

 这样,无论存储哪种传感器的数据,都只需要占用一份内存空间。

六、注意事项

6.1. 内存布局与对齐

  • 内存共享:共用体的所有成员共享同一块内存空间。因此,修改一个成员的值会影响其他成员的值,因为它们实际上占用的是同一段内存。
  • 内存对齐:编译器可能会为了内存对齐在共用体的成员之间插入填充字节。可能导致共用体的大小大于其最大成员的大小。内存对齐是为了提高处理器访问内存的效率,但可能会增加内存使用。
  • 大小端问题:不同硬件平台可能采用不同的大小端模式(大端或小端)。在编写涉及共用体的跨平台代码时,需要注意大小端问题,确保数据在不同平台上的正确性。

6.2. 访问与修改成员

  • 避免同时访问多个成员:由于共用体的成员共享内存,因此同时访问多个成员是没有意义的,因为它们的值会相互覆盖。在编程时应避免这种情况。
  • 使用正确的运算符:访问共用体成员时,应使用.运算符(对于共用体变量)或->运算符(对于指向共用体的指针)。

6.3. 函数参数与返回值

  • 不能作为函数参数或返回值:在标准C语言中,共用体变量不能直接作为函数参数传递,也不能作为函数的返回值。但可以使用指向共用体变量的指针作为函数参数或返回值。
  • 传递指针而非值:如果需要在函数内部修改共用体的成员,应传递指向共用体的指针而非其值。这样可以避免在函数调用过程中进行不必要的内存复制,并允许函数直接修改原始数据。

6.4. 类型安全性

  • 类型转换:虽然共用体可以实现不同类型数据之间的转换,但这种转换是隐式的,不进行类型检查。因此,在使用共用体进行类型转换时需要谨慎,确保转换是安全的。
  • 避免类型混淆:在编写涉及共用体的代码时,应清晰地区分不同成员的类型和用途,避免类型混淆导致的错误。

6.5. 初始化方式

共用体的初始化方式与结构体有所不同。只能对共用体的第一个成员进行初始化,例如:

union Data {
    int i;
    float f;
};
union Data data = {10}; // 初始化共用体的第一个成员i为10

如果要初始化其他成员,需要在定义共用体变量后单独进行赋值操作。 

6.6. 访问时机与数据一致性

由于共用体成员共享内存,访问成员的时机非常重要。在对一个成员赋值后,如果没有及时访问该成员,而是先访问了其他成员,可能会得到未定义的结果。例如:

union Data {
    int i;
    char c;
};
union Data data;
data.i = 0x12345678;
// 先访问data.c,此时data.c的值可能是不确定的
printf("%x\n", data.c);
// 再访问data.i,其值可能已经因为之前对data.c的访问而改变
printf("%x\n", data.i);

6.7. 编程实践

  • 明确用途:在定义共用体之前,应明确其用途和成员类型。确保共用体的设计符合实际需求,避免不必要的复杂性。
  • 注释与文档:由于共用体的使用可能增加代码的复杂性,因此应在代码中添加足够的注释和文档,以便其他开发人员理解其用途和工作方式。
  • 测试与验证:在编写涉及共用体的代码后,应进行充分的测试和验证,确保其在不同平台和条件下的正确性。

七、总结

共用体(Union)在嵌入式C语言中是一种特殊的数据结构,允许在相同的内存位置存储不同类型的数据。主要用于节省内存空间、数据类型转换以及访问硬件寄存器等场景。使用共用体时需注意内存对齐、避免同时访问多个成员等问题。总之,共用体是嵌入式编程中一种灵活且强大的工具。

八、参考文献

  • 《C Primer Plus》:由 Stephen Prata 所著。
  • 《C 和指针》:从基本数据类型讲起,涵盖控制结构、运算符、表达式、指针、数组、函数、内存管理等,内容全面且对初学者友好,也包含共用体相关知识。
  • 《C 专家编程》:深入讲解 C 语言的高级特性和编程技巧,包括共用体在不同场景下的应用方式等内容,适合有一定 C 语言基础的读者提升编程能力。
  • 《C 陷阱与缺陷》:从实践出发,讲解 C 语言中常见的陷阱和缺陷,其中可能涉及共用体使用过程中容易出现的问题及解决方案和技巧,适合有一定 C 语言编程经验的读者。
  • 《嵌入式系统软件设计》:作者是 Michael J. Pont,介绍了嵌入式系统的基础知识和应用开发过程中的常见问题及解决方案。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/962720.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Unet 改进:在encoder和decoder间加入TransformerBlock

目录 1. TransformerBlock 2. Unet 改进 3. 完整代码 Tips:融入模块后的网络经过测试,可以直接使用,设置好输入和输出的图片维度即可 1. TransformerBlock TransformerBlock是Transformer模型架构的基本组件,广泛应用于机器翻译、文本摘要和情感分析等自然语言处理任务…

音标-- 01--音标

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 国际音标1.元音音标单元音双元音常见单词 2.辅音音标清辅音&#xff0c;浊辅音清辅音&#xff08;不振动&#xff09;和 浊辅音&#xff08;振动&#xff09;是成对…

使用Pygame制作“打砖块”游戏

1. 前言 打砖块&#xff08;Breakout / Arkanoid&#xff09; 是一款经典街机游戏&#xff0c;玩家控制一个可左右移动的挡板&#xff0c;接住并反弹球&#xff0c;击碎屏幕上方的砖块。随着砖块被击碎&#xff0c;不仅能获得分数&#xff0c;还可以体验到不断加速或复杂的反弹…

【AI绘画】MidJourney关键词{Prompt}全面整理

AI绘画整理&#xff0c;MidJourney关键词。喜欢AI绘画的朋友必备&#xff0c;建议收藏&#xff0c;后面用到时供查阅使用。 1、光线与影子篇 中 英 闪耀的霓虹灯 shimmeringneon lights 黑暗中的影子 shadows in the dark 照亮城市的月光 moonlightilluminatingthe cit…

嵌入式系统|DMA和SPI

文章目录 DMA&#xff08;直接内存访问&#xff09;DMA底层原理1. 关键组件2. 工作机制3. DMA传输模式 SPI&#xff08;串行外设接口&#xff09;SPI的基本原理SPI连接示例 DMA与SPI的共同作用 DMA&#xff08;直接内存访问&#xff09; 类型&#xff1a;DMA是一种数据传输接口…

AVL树介绍

一、介绍 高度平衡的搜索二叉树&#xff0c;保证每个节点的左右子树高度差不超过1&#xff0c;降低搜索树的高度以提高搜索效率。 通过平衡因子和旋转来保证左右子树高度差不超过1 二、插入节点 1、插入规则 &#xff08;1&#xff09;搜按索树规则插入节点 &#xff08;…

win11 sourcetree安装问题

win11 sourcetree安装出现msys-2.0.dll 问题&#xff0c;需要从win10的以下路径复制出 msys-2.0.dll来加入到win11中 C:\Users\kz121468\AppData\Local\Atlassian\SourceTree\git_local\usr\bin\ 复制到 win11的 C:\Users\kz121468\AppData\Local\Atlassian\SourceTree\git_lo…

Contrastive Imitation Learning

机器人模仿学习中对比解码的一致性采样 摘要 本文中&#xff0c;我们在机器人应用的对比模仿学习中&#xff0c;利用一致性采样来挖掘演示质量中的样本间关系。通过在排序后的演示对比解码过程中&#xff0c;引入相邻样本间的一致性机制&#xff0c;我们旨在改进用于机器人学习…

Spring Web MVC基础第一篇

目录 1.什么是Spring Web MVC&#xff1f; 2.创建Spring Web MVC项目 3.注解使用 3.1RequestMapping&#xff08;路由映射&#xff09; 3.2一般参数传递 3.3RequestParam&#xff08;参数重命名&#xff09; 3.4RequestBody&#xff08;传递JSON数据&#xff09; 3.5Pa…

DeepSeek的使用技巧介绍

DeepSeek是一款由杭州深度求索人工智能技术有限公司开发的AI工具&#xff0c;结合了自然语言处理和深度学习技术&#xff0c;能够完成多种任务&#xff0c;如知识问答、数据分析、文案创作、代码开发等。以下将从使用技巧、核心功能及注意事项等方面详细介绍DeepSeek的使用方法…

创新创业计划书|建筑垃圾资源化回收

目录 第1部分 公司概况........................................................................ 1 第2部分 产品/服务...................................................................... 3 第3部分 研究与开发.................................................…

为AI聊天工具添加一个知识系统 之80 详细设计之21 符号逻辑 之1

本文要点 要点 前面我们讨论了本项目中的正则表达式。现在我们将前面讨论的正则表达式视为狭义的符号文本及其符号规则rule&#xff08;认识的原则--认识上认识对象的约束&#xff09;&#xff0c;进而在更广泛的视角下将其视为符号逻辑及其符号原则principle&#xff08;知识…

Spring Boot 热部署实现指南

在开发 Spring Bot 项目时&#xff0c;热部署功能能够显著提升开发效率&#xff0c;让开发者无需频繁重启服务器就能看到代码修改后的效果。下面为大家详细介绍一种实现 Spring Boot 热部署的方法&#xff0c;同时也欢迎大家补充其他实现形式。 步骤一、开启 IDEA 自动编译功能…

ARM嵌入式学习--第十一天(中断处理 , ADC)

--中断的概念 中断是指计算机运行过程中&#xff0c;出现某些意外情况需主机干预时&#xff0c;机器能自动停止正在运行的程序并转入处理新情况的程序&#xff0c;处理完毕后又返回被暂停的程序继续运行 --CPU处理事情的方式 -轮询方式 不断查询是否有事情需要处理&#xff0c…

ARM嵌入式学习--第十天(UART)

--UART介绍 UART(Universal Asynchonous Receiver and Transmitter)通用异步接收器&#xff0c;是一种通用串行数据总线&#xff0c;用于异步通信。该总线双向通信&#xff0c;可以实现全双工传输和接收。在嵌入式设计中&#xff0c;UART用来与PC进行通信&#xff0c;包括与监控…

socket实现HTTP请求,参考HttpURLConnection源码解析

背景 有台服务器&#xff0c;网卡绑定有2个ip地址&#xff0c;分别为&#xff1a; A&#xff1a;192.168.111.201 B&#xff1a;192.168.111.202 在这台服务器请求目标地址 C&#xff1a;192.168.111.203 时必须使用B作为源地址才能访问目标地址C&#xff0c;在这台服务器默认…

漏洞扫描工具之xray

下载地址&#xff1a;https://github.com/chaitin/xray/releases 1.9.11 使用文档&#xff1a;https://docs.xray.cool/tools/xray/Scanning 与burpsuite联动&#xff1a; https://xz.aliyun.com/news/7563 参考&#xff1a;https://blog.csdn.net/lza20001103/article/details…

正月初三特殊的一天

在我们河南豫东地区&#xff0c;初三这一天一般情况下可以在家休息&#xff0c;不需要串门走亲戚&#xff0c;给亲戚的长辈或比自己辈份长的拜年。 特殊的正月初三 还有两种情况&#xff0c;正月初三这一天必须去走亲戚。一种是有去世的亲戚没有过三周年&#xff0c;正月初三这…

强化学习笔记——4策略迭代、值迭代、TD算法

基于策略迭代的贝尔曼方程和基于值迭代的贝尔曼方程&#xff0c;关系还是不太理解 首先梳理一下&#xff1a; 通过贝尔曼方程将强化学习转化为值迭代和策略迭代两种问题 求解上述两种贝尔曼方程有三种方法&#xff1a;DP&#xff08;有模型&#xff09;&#xff0c;MC&#xff…

HTTP协议和静态web服务器

一、HTTP协议 1 HTTP协议的定义 网络协议 网络协议是指计算机通信网络中两台计算机之间进行通信所必须共同遵守的规定或规则。HTTP协议 HTTP协议(超文本传输协议)是一种网络通信协议,它允许将超文本标记语言(HTML)文档从Web服务器传送到客户端的浏览器。默认端口:80HTTPS协…