大家可以将下面的程序copy到vc6.0中编译运行下,看看最终 A 与 B变量的内存比较结果是否相同?
可能大部分人的结果都是相同的。
typedef struct tagChipReg
{
unsigned char a;
unsigned short b;
}CHIP_REG;
CHIP_REG A,B;
extern void swap(char &a,char &b);
int main(int argc, char* argv[])
{
int iCmpRslt = 1;
A.a = 1;
A.b = 2;
B.a = 1;
B.b = 2;
}
但是笔者这里要提醒下,其实这个代码是不安全的:比较结果未必每次都相同。
为什么?
要回答这个问题就不得不说说字节对齐的问题了。
在嵌入式软件领域中,程序员要想写好程序,就不得不知道写硬件相关的知识,否则写出来的程序可能今天、明天运行的结果是对的,但是后天就不一定了。
本例中,我们要知道程序在运行时的状态,在编译器完成编译的时候,会根据CPU访问内存的取值方式不同,产生不同的汇编代码。大多数CPU取值是四字节对齐取值的,这样才会有较高的存储效率,因为现在大多数的CPU是32位的数据线宽度。所以编译器为了产生更高效的代码,多会讲结构体类的数据按照四字节对齐的方式存放到内存。于是上例中的结构体在内存中实际上就是按照下面的形式来存储的:
地址0:/ a /
地址1:/无效值 /
地址2:/b的高8位/
地址3:/b的低8位/
可见有个地址1中存在一个不确定的值。正是由于这个值的存在,才会导致上例中memcmp的结果存在不确定性。
那么如何解决这个问题?
把这个不确定的值拿掉不就行了吗?答案是肯定的。我们可以通过将结构体强制1字节对齐就可以有效避免这个问题了,但是这个办法在CPU需要大量访问A、B的情况下变得效率低下。更好的办法是判断两个结构体变量是否相同,只判断其每个成员是否完全相同,而不是用memcmp。
大家觉得如何?