用一个例子简单说明一下(V6环境):
输出结果:
char:1 long:4
s1: 8 s2: 16
结果分析(栈由高向低增长,小端字节序):
addr(s1.l): 0x12ff7c
addr(s1.s): 0x12ff78
addr(s1): 0x12ff78
addr(s2.l): 0x12ff74
addr(s2.t.l): 0x12ff70
addr(s2.t.s): 0x12ff6c
addr(s2.t): 0x12ff6c
addr(s2.s): 0x12ff68
Press any key to continue
简图为:
| | 0x12ff80
| |
| |
| |
s1.l -> | | 0x12ff7c (s1.l占4个字节)
| |
| |
| |
s1.s (s1)-> | | 0x12ff78 (s1.s占4个字节,s1占4+4=8个字节)
| |
| |
| |
s2.l -> | | 0x12ff74 (s2.l占4个字节)
| |
| |
| |
s2.t.l -> | | 0x12ff70
| |
| |
| |
s2.t.s(s2.t) -> | | 0x12ff6c (s2.t占8个字节)
| |
| |
| |
s2.s -> | | 0x12ff68 (s2.s占4个字节,s2占4+8+4=16个字节)
附转四个重要的基本概念:
1.数据类型自身的对齐值(32位机):
对于char型数据,其自身对齐值为1,对于short型为2,对于int和float类型,其自身对齐值为4,对于double类型,其自身对齐值为8,单位字节。
2.结构体或者类的自身对齐值:其成员中自身对齐值最大的那个值。
3.指定对齐值:#pragma pack (value)时的指定对齐值value。
4.数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中小的那个值。
有了这些值,我们就可以很方便的来讨论具体数据结构的成员和其自身的对齐方式。有效对齐值N是最终用来决定数据存放地址方式的值。有效对齐N,就是表示“对齐在N上”,也就是说该数据的"存放起始地址%N=0"。而数据结构中的数据变量都是按定义的先后顺序来排放的。第一个数据变量的起始地址就是数据结构的起始地址。结构体的成员变量要对齐排放,结构体本身也要根据自身的有效对齐值圆整(就是结构体成员变量占用总长度需要是对结构体有效对齐值的整数倍,结合例子理解)。这样就不难理解上面的例子的值了。
注意:在x86上,从奇地址访问变量类似的操作只会影响效率,但是在MIPS或者sparc上,可能就是一个error,因为它们要求必须字节对齐。
如果出现对齐或者赋值问题首先查看:
1. 编译器的big little端设置。
2. 看这种体系本身是否支持非对齐访问。
3. 如果支持,查看设置了对齐与否,如果没有则查看访问时需要加某些特殊的修饰来标志其特殊访问操作。
下面是一个小例子,这个例子涉及字节大小端、字节非对齐访问问题。