以下是《嵌入式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协议斩,不让用联合,也不让用指针类型转换??!!有什么更好的方法么??