以下是《嵌入式C 标准研究报告》中关于MISRA对联合体的使用约束说明
规则 18.4 不允许使用联合体 这事一个不太近情理的规定,在具体阐述为何MISRA-C:2004如此痛恨联合体之前,首先需要明确与联合体相关的细节: 1)联合体的末尾有多少个填充单元 2)联合体的各个成员如何对齐 3)多字节的数据类型高低字节如何排放顺序 4)如果包含位字段 (bit-field),各位如何排放
针对细节3举个例子 程序段2.1
typedef union{ uint32_t word; uint8_t bytes[4]; }word_msg_t;
uint32_t read_msg(void) { word_msg_t tmp; /* tmp.byte[0] 对应于tmp.word的高8位 tmp.byte[1]对应于tmp.word的次高8位,依此类推 */ tmp.bytes[0] = read_byte(); tmp.bytes[1] = read_byte(); tmp.bytes[2] = read_byte(); tmp.bytes[3] = read_byte(); return (tmp.word); }
以上代码在各种通信协议中使用的频率很高,接收端接收到的数据一般都以字节为单位存放,主控程序需要根据相应的协议将接受到的多个字节进行组合。为了实现相同的功能,MISRA-C:2004推荐恶劣read_msg()函数的另外一种写法.
程序段2.2
uint32_t read_msg(void) { uint32_t word; word = ((uint32_t)read_byte() ) < < 24; word = word | (((uint32_t)read_byte()) < < 16); word = word | (((uint32_t)read_bytes()) < < 8); word = word | ((uint32_t)read_byte()); return (word); } 无论从程序的可读性还是从执行效率来讲,程序段2.1都要优于程序段2.2。然而,程序段2.1在Intel 80x86/Pentium体系(little-endian,存储多字节整数的时候低位字节存放在低地址)CPU和在Motorola 68K体系(big-endian)中的执行结果完全不一样。假设read_byte()函数返回的数据依次是0x01,0x02,0x03,0x04,则在Intel体系中,程序段2.1 read_msg返回的值是0x4321,在Motorola体系中,返回的则是0x1234. 无论在Intel体系还是在Motorola体系中,程序段 2.2 中read_msg的返回值都是0x1234
这样的话在处理通信协议的时候真的是很麻烦了,比如我要写一个TCP/IP协议斩,不让用联合,也不让用指针类型转换??!!有什么更好的方法么?? |