【ARM 嵌入式 C 入门及渐进 5 -- 共用体 Union 与 结构体位域介绍】

1.1 共用体 Union

结构体占用的内存大于等于所有成员占用的内存的总和(成员之间可能会存在缝隙);
共用体占用的内存等于最长的成员占用的内存。

共用体使用了内存覆盖技术,同一时刻只能保存一个成员的值,所有的数据成员具有相同的起始地址,如果对新的成员赋值,就会把原来成员的值覆盖掉。

参见 linux spin_lock 结构体实现
linux/arch/arm64/include/asm/spinlock_types.h

typedef struct {
        union {
                u32 slock;
                struct __raw_tickets {
#ifdef __ARMEB__
                        u16 next;
                        u16 owner;
#else
                        u16 owner;
                        u16 next;
#endif
                } tickets;
        };
} arch_spinlock_t;

1.1.1 位域结构

位域出现的原因是由于某些信息的存储表示只需要几个 bit 位就可以表示而不需要一个完整的字节,同时也是为了节省存储空间和方便处理:

struct 位域结构名
{
    类型说明符  位域名:位域长度
}

例子:

struct  bit_struct
{
    int  bit1:3;
    int  bit2:5;
    int  bit3:7;
} data;

其中 bit_struct 表示位域结构体,bit1、bit2、bit3 表示对应的位域,data 表示位域结构体定义的变量。整个位域结构体占用 2 个字节:

  • bit1 占 3 位;
  • bit2 占 5 位,bit1 和 bit2 共用一个字节;
  • bit3 占 7 位,独占一个字节。

说明:

  1. 位域必须存储在同一个类型中,不能跨类型,同时也说明位域的长度不会超过所定义类型的长度。如果一个定义类型单元里所剩空间无法存放下一个域,则下一个域应该从下一单元开始存放。

    例如:在 ARM32 架构下, 一个 int 类型占用 32 bits,假设用掉了 25 bits,还剩7 bits,这时要存储一个 8 bits 的位域元素,那么这个元素就只能从下一个 int 类型的单元开始而不会在前面一个 int 类型中占7为后面的 int 类型中占 1 位。
  2. 如果位域的位域长度为 0 表示是个空域,同时下一个域应当从下一个字节单元开始存放。
  3. 使用无名的位域来作为填充和调整位置,切记该位域是不能被使用的。
  4. 位域的本质上就是一种结构体类型,不同的是其成员是按二进制位来分配的。

1.1.2 共用体中嵌套结构体

案例一 (机器为小端模式):

#include <stdio.h> 

typedef union
{
    unsigned int u;
    struct
    {
        unsigned char a :1;
        unsigned char b :1;
        unsigned char c :6;
        unsigned char d :1;        
    } ST;
} UN_t;

static int main(void)  
{
    UN_t un;
    
    un.u = 0;  
    un.ST.a = 1;   
    un.ST.b = 2;  
    un.ST.c = 3;  
    un.ST.d = 4;  
    printf("%d\n", un.u);  
   
    return 0;  
} 

1)注意联合体的定义,就是组成联合体的变量共用一个空间, 这个 例子中变量 uST 共用一个空间;
3)基于小端结构, 数据的低字节保存在内存的低地址中, ST 占用 9 bits, 与变量 u(32Bit)共用低位的 9 位。
4)根据小段结构,变量 a 的地址应该最低,往后依次是 b, c, d
5)un.u = 0; 执行这一步,变量对应空间二进制全部为 0x0,即 0b00000000 00000000 00000000 00000000
6)un.ST.a = 1; 执行这一步,变量最后一位变化,即 0b00000000 00000000 00000000 00000001;
7)un.ST.b = 2; 执行这一步,由于1 bit 空间无法存储 2,所以赋值被截断,原值不变;
8)un.ST.c = 3; 执行这一步,变量第 3-8 bit 发生变化,变量值变为 0b00000000 00000000 00000000 00001101;
9)un.ST.d = 4; 执行这一步,由于 1 位空间无法存储 4,所以赋值被截断,原值不变;
10)所以最终的结果就是: 0b00000000 00000000 00000000 00001101
11)printf("%d\n", un.u); 输出结果就是13

案例二 I2C 驱动中对位域的使用:
unsigned 也是占用 32bit 空间。

typedef union {
	unsigned value; //bit[31-0]
    struct {
        unsigned data : 8;          /* Data:占用第8bit bit7-0 */
        unsigned icr_start : 1;     /* ICR Start 占用第8bit
        unsigned icr_stop : 1;      /* ICR Stop 占用第9bit
        unsigned icr_acknak : 1;    /* ICR ACKNAK 占用第10bit
        unsigned icr_tb : 1;        /* ICR Transfer Byte 占用第11bit
        unsigned reserved : 20;     /* Unused bit[12-31]
    } s;
    
} fifo_entry_t;

svalue 共用 32bit
前 8 bit (0-7bit) 由于发送 data 数据;
第 8 bit 用于控制 I2C Controller 的 start 信号;
第 9 bit 用于控制 I2C Controller 的 stop 信号;
第 10 bit 用于控制 I2C Controller 的 ACK信号;
第 11 bit 用于控制 I2C Controller 启动发送;
第 12-31 bits 没有使用。

需要注意的是共用体变量不能初始化:

下面错误做法:

union data
{
    int i;
    char ch;
    float f;
} a = { 1, 'a', 1.5 } ;
  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
C语言共用体Union)和位域(Bit-Field)是用来优化内存使用和灵活操作数据的工具。 共用体是一种特殊的数据类型,它允许不同的变量共享同一块内存空间。共用体的成员变量共享同一内存,占用内存大小等于最大成员的大小。通过修改共用体的一个成员变量的值,可以影响到其他成员变量的值。共用体适用于在不同的数据类型之间进行转换或者存储占用内存大小不定的数据。 位域是一种特殊的结构体成员变量,可以指定成员变量占用的位数,从而实现对内存空间的灵活利用。位域的成员变量必须是整型数据类型,并且位域的大小不能超过该整型类型的大小。位域可以用于减小数据结构占用的内存大小,以及进行数据的位操作。 共用体位域结构体可以一起使用。通过在位域结构体定义共用体成员变量,可以实现对内存的灵活使用和数据的高效操作。共用体可以用于存储不同类型的数据,而位域可以用于压缩数据的存储空间。这种结合使用的方式可以为我们的程序带来更加高效和节省内存的特点。 总结起来,C语言共用体位域结构体提供了一种优化内存使用和操作数据的方式。它们可以灵活地对内存空间进行利用,并且能够高效地操作数据。通过合理的使用共用体位域结构体,我们可以达到节省内存和提高程序执行效率的目的。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

主公CodingCos

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值