在codeWarrior编译的程序 和 win7下vs10编译的程序 利用24L01无线模块进行通讯时.除了学到了 二者程序存储的机器字节顺序不同,在使用如short这样的大于1字节的变量类型 需要进行字节的逆序 以外 ,在编写协议的时候想用结构体简化程序,减少使用memcpy, 但我知道结构体在编译的时候编译器一般存在一些“字节对齐”规则(包括了align of member 和 structure alignment) ,之前只是很马虎的了解了一下,今天又认真了解了“字节对齐”.
为了简化叙述,这里用 [n 这个符号代表一个成员变量 相对于他所在的结构体的首地址的offset字节数.
(1) align of member
align of member 其实也就是在决定每个成员变量的 [n
而它完全由 该成员变量的对齐值决定.
而某一个成员变量的对齐值 = min( pack setting value, sizeof(该成员变量) )
对于vs10,可以用#pragma pack(show) 宏命令显示 pack setting value 的值(在vs上一般是8)
(在codeWarrior 5.9.0里#pragma pack 语句编译时会被显示警告,意思是没有理会该宏,所以对于codeWarrior 对“字节对齐”的规定是否遵循一般的规则 也很难得知,自己也只能靠实践去找出)
对齐值怎么决定每个成员变量的 [n 呢?
对于vs10,编译器按照
" 一个成员变量的 [n 必须为 该成员变量的对齐值 的 整数倍"
这一规则去决定的.
看一个例子:(程序中#pragma pack(8)是为了保证pack setting value = 8 ,不过vs10默认就是8, 最后#pragma pack()是为了还原上一次pack setting value)
#pragma pack(8) struct S { char a;//[0 short b;//[2 char c;//[4 int f ;//[8 char e ;//[12 }; #pragma pack()
编译以后,
对于 a的对齐值 = min(8,1) = 1 ; 编译器会选择把a放在[0 处满足 0%1 == 0
对于 b的对齐值 = min(8,2) = 2; 编译器会选择把b放在 [2 处才满足 2%2 == 0 (这就和a隔了一个字节)
对于 c的对齐值 = min(8,1) = 1 ; 编译器会选择把c放在 [4 处满足 4%1 == 0 (紧挨着b)
对于 f的对齐值 = min(8,4) = 4; 编译器会选择把f放在 [8 处才满足 8%4 == 0 (这就和c 隔了3个字节)
对于 e的对齐值 = min(8,1) = 1 ; 编译器会选择把e放在 [12 处满足 12%1 == 0 (紧挨着f)
所以仅就这些成员变量所占的空间来算 以上成员占用13字节 ,但是sizeof(S) 却= 16
这就需要下一条规则来解释:
(2)structure alignment
对于structure的长度有一条规定:
"结构体的长度 应该是 max( max(结构体各成员的对齐值) ,align(#)设置的值 ) 的整数倍"
其中,align(#)设置的值是指 指令: __declspec(align(n )) 当中的n, n的取值为2的1次方至2的8192次方.
大多数情况不用考虑这条指令,对于上面的代码,它用起来看起来会是这个样子:
__declspec( align(32) )struct S { char a;//[0 short b;//[2 char c;//[4 int f ;//[8 //char e; };
我也没用过, 更多详情可以再去搜索.
当然,第一段代码并没有使用这个奇怪的指令,所以max( max(结构体各成员的对齐值) ,align(#)设置的值 ) 就是 max(结构体各成员的对齐值) ,容易看出,第一段代码中,int f ;的对齐值最大 = 4.
所以 ,编译器要使结构体S 占用4的整数倍的字节. 这就可以解释 sizeof(S) = 16 而非 13字节.
对于这篇文章,我主要参考学习了这个地址:
http://www.cnblogs.com/flying_bat/archive/2008/04/23/1167611.html
by ga6840