使用Union访问同一变量不同维度违反MISRA,求合规实现方案
MISRA合规的同变量多宽度访问替代方案
你当前使用union做类型双关的实现违反了MISRA C:2012 规则19.2,该规则禁止读取union中未被最后写入的成员,C标准也明确该行为属于未定义行为,只是在你的当前编译平台下刚好符合预期。以下是两种合规的替代方案,可实现完全相同的业务效果:
方案1:显式字节序转换(最推荐)
该方案完全符合MISRA所有规则,且可以显式控制SPI通信的字节序,不受平台大小端影响,可移植性最强。
实现代码
// 计数器变量直接定义为16位无符号数,正常处理计数、溢出逻辑 uint16_t totalProgressiveNumber; // 按SPI通信要求的字节序(示例为大端/高字节在前,可根据需求调整)将16位值转换为8位数组 void ProgressNum_toByteArray(uint16_t num, uint8_t *arr) { arr[0] = (uint8_t)((num >> 8U) & 0xFFU); arr[1] = (uint8_t)(num & 0xFFU); } // 将SPI接收的8位数组转换回16位计数器值 uint16_t ProgressNum_fromByteArray(const uint8_t *arr) { return (uint16_t)(((uint16_t)arr[0] << 8U) | (uint16_t)arr[1]); }
使用方式
- 计数、溢出处理逻辑直接操作
totalProgressiveNumber即可,和原有逻辑完全一致 - SPI发送前调用
ProgressNum_toByteArray将当前计数器值转成8位数组用于传输 - SPI接收完成后调用
ProgressNum_fromByteArray将接收的数组转成16位值更新计数器
方案2:字符指针直接访问字节(零开销,和原实现行为完全一致)
如果需要完全保留原union实现的运行效率、不需要跨平台兼容,可使用MISRA允许的字符指针访问对象字节表示的方式实现,无任何运行时开销。
实现代码
// 计数器变量直接定义为16位无符号数 uint16_t totalProgressiveNumber; // 定义指向计数器字节的8位指针,访问效果和原progressNumberArr完全一致 uint8_t * const progressNumberArr = (uint8_t *)&totalProgressiveNumber;
注意事项
- 该方案的字节顺序和平台大小端完全绑定,和你原有union实现的运行行为没有任何差异
- 符合C标准定义、无未定义行为,同时满足MISRA规则要求(MISRA允许将对象指针转换为字符类型指针访问对象的字节表示)
内容的提问来源于stack exchange,提问作者buch
相关产品推荐
相关产品推荐

