C语言应用笔记:能降低结构体内存占用的位域操作

yumo6662小时前技术文章1

在C语言中,结构体位域(Bit Fields)允许精确控制结构体成员占用的内存位数,这在嵌入式系统、网络协议等内存敏感场景中非常有用。以下是位域的核心知识点和示例:

位域定义语法

struct 结构体名 {
类型 [成员名] : 位宽;
};
  • 类型:必须为整型(intunsigned intsigned int
  • 位宽:指定成员占用的比特数(0 ≤ 位宽 ≤ 类型位数)
  • 匿名位域:无成员名的位域,用于占位填充(如 unsigned int : 4;

关键特性

  • 内存压缩:多个位域成员可能共享同一存储单元
  • 地址不可取:无法对位域成员使用取地址运算符 &(因小于1字节)
  • 赋值行为:无法赋予超出bit位的值

基础示例

#pragma pack(1) // 1字节对齐

typedef struct {
  	// 占1个字节
    uint8_t byte;
		// 共占4个字节
    uint32_t a : 2;
    uint32_t b : 4;
    uint32_t c : 6;
} example;

#pragma pack()

static example t = {
    .a = 2,  .b = 5, .c = 6,
};

int main(void) {
    printf("example size: %d\r\n", sizeof(example));
    printf("a = %d\r\n", t.a);
    printf("b = %d\r\n", t.b);
    printf("c = %d\r\n", t.c);
    return 0;
}

高级用法

将原始数据拷贝给带位域成员的结构体变量,可自动解析出占用不同bit位的数值,省去了手动解析的步骤。具体实现如下:

#pragma pack(1)

// 定义占用2字节的位域结构体
typedef struct {
    uint16_t a : 5; // 5位 (0-31)
    uint16_t b : 7; // 7位 (0-127)
    uint16_t c : 4; // 4位 (0-15)
} BitFieldStruct;

#pragma pack()

int main() {
    // 定义位域结构体变量
    BitFieldStruct data;
    // 十六进制值: 1010 1011 1100 1101
    uint16_t raw_value = 0xABCD;
    // 使用内存拷贝确保正确赋值
    memcpy(&data, &raw_value, sizeof(data));
    // 打印结果
    printf("原始值: 0x%04X (%d)\n", raw_value, raw_value);
    printf("结构体大小: %d 字节\n", sizeof(data));
    printf("解析结果:\n");
    printf("  a (5位): %d\n", data.a); // 低5位: 0b01101 = 13
    printf("  b (7位): %d\n", data.b); // 中间7位: 0b1011110 = 94
    printf("  c (4位): %d\n", data.c); // 高4位: 0b1010 = 10
    return 0;
}

输出结果

原始值: 0xABCD (43981)
结构体大小: 2 字节
解析结果:
 a (5位): 13
 b (7位): 94
 c (4位): 10

相关文章

数据结构-位运算_数据结构按位查找

左移( << ):操作数的非0位左移n位,低位补0右移( >> ):操作数的非0位右移n位,高位补0无符号右移( >>> ):正数右移,高位用0补,负数右移,...

PLC的位逻辑运算指令_plc中的位怎么理解

PLC(可编程逻辑控制器)的位指令是针对单个二进制位(0 或 1)进行操作的基础指令,主要用于逻辑控制,是梯形图(LD)编程中最常用的指令类型。以下是 PLC 位指令的核心类别及常用指令:常开常闭输出...

【C语言·015】逗号运算符的求值顺序与返回值规则

很多人第一次看到 , 都把它当“分隔符”:函数实参之间的逗号、初始化列表里的逗号……但在表达式里,, 还有另一个身份——逗号运算符。它既能强制求值顺序,又能控制返回值,是解决副作用与顺序问题的一把小刀...

C 语言指针全解析:从门牌号到内存黑魔法,一文带你彻底搞懂!

很多人一提到 C 语言指针 就皱眉:“指针是不是地址?”“数组和指针是不是一样的?”“为什么 * 有时候是解引用,有时候是乘法?”其实指针没那么神秘。只要把它拆开理解,就会发现它不过是一串数字,存的就...

C语言应用笔记:整数字节大小端翻转

在C语言中,实现大小端(Endian)翻转可以通过位操作或内存操作完成。以下提供两种常用方法:方法1:位运算(推荐)通过移位和掩码操作直接交换字节位置:// 16位翻转(安全版本) #define S...

C语言应用笔记:获取结构体成员偏移地址

在C语言中,获取结构体成员的偏移地址(即成员相对于结构体起始地址的字节偏移量)有两种常用方法:1. 使用标准库宏 offsetof(推荐)<stddef.h> 头文件提供了 offseto...