You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

嵌入式C语言中如何创建占用3字节的24位无符号整数?

解决24位无符号整数结构体占用3字节的问题

首先得搞清楚为什么你用位域实现的uint24_t会占4字节——这和C语言中位域的存储规则以及结构体对齐直接相关。你用uint32_t作为位域的基础类型,编译器会把整个位域放在一个uint32_t大小的存储单元里(也就是4字节),哪怕你只用到了其中24位,剩下的8位会被当作填充保留。再加上结构体默认会对齐到其最大成员的对齐要求(这里uint32_t的对齐要求是4字节),所以整个结构体的大小就被拉到了4字节。而用uint8_t[3]的结构体没有这个问题,因为uint8_t的对齐要求是1字节,三个成员加起来正好3字节,不需要额外填充。

接下来给你几个可行的解决方案,各有优劣,你可以根据嵌入式系统的需求选择:

1. 用编译器属性强制取消对齐填充

GCC、Clang这类编译器支持__attribute__((packed))属性,它可以让结构体跳过自动对齐填充。修改你的类型定义:

typedef struct __attribute__((packed)) {
    uint32_t v : 24;
} uint24_t;

这样再用sizeof(uint24_t)得到的就是3字节了。但要注意一个关键风险:非对齐的内存访问在很多嵌入式架构(比如ARM Cortex-M系列部分核心、RISC-V)上要么会触发硬件异常,要么会导致性能下降。因为你访问v成员时,本质上是在访问一个非4字节对齐的32位数据,这可能会出问题。如果你的系统严格禁止非对齐访问,这个方法要谨慎使用。

2. 用字节数组模拟24位整数

这是更安全的方案,直接用3个uint8_t组成的数组存储,自己实现数值的读写逻辑,完全避免对齐问题。定义方式:

typedef uint8_t uint24_t[3];

然后写辅助函数处理数值转换(注意字节序,下面是大端模式的例子,可根据你的系统字节序调整):

// 将24位无符号值写入uint24_t变量
void uint24_set(uint24_t dest, uint32_t value) {
    dest[0] = (value >> 16) & 0xFF; // 高字节
    dest[1] = (value >> 8) & 0xFF;  // 中字节
    dest[2] = value & 0xFF;         // 低字节
}

// 从uint24_t变量读取24位无符号值
uint32_t uint24_get(const uint24_t src) {
    return ((uint32_t)src[0] << 16) | ((uint32_t)src[1] << 8) | src[2];
}

这种方式的好处是绝对占用3字节,而且每次访问都是字节级的,不会触发对齐问题。缺点是不能像普通整数那样直接用+-等运算符,需要自己封装运算函数,或者每次转换为uint32_t再运算。

3. 使用联合体结合packed属性(折中方案)

如果你既想保留位域的直观性,又要控制大小,可以用联合体:

typedef union __attribute__((packed)) {
    struct {
        uint32_t v : 24;
    };
    uint8_t bytes[3];
} uint24_t;

这样你既可以通过v成员直接访问24位数值(同样要注意非对齐访问的风险),也可以通过bytes数组按字节操作。不过本质上还是依赖packed属性,对齐相关的风险依然存在。

最后再提醒一句:在RAM极度紧张的嵌入式系统中,除了数据类型的大小,还要考虑内存访问的效率和稳定性。如果你的系统对非对齐访问敏感,优先选择字节数组的方案,虽然麻烦一点,但更可靠。

内容的提问来源于stack exchange,提问作者bgarrood

火山引擎 最新活动