前不久,在C++程序中碰到一个有关结构体字节对齐的问题。
一。问题描述
在程序中,定义了一个结构体,如下:
typedef struct
{
char name[33];
int ID;
int age;
} PERSON;
声明了一个该结构体的数组:
PERSON peo[30];
当从结构体中取出ID字段给一个int类型的局部变量赋值时,却出现异常.
比如结构体中的字段都已经有初始值
peo[0].ID =4;
下面的赋值语句
int tempID = peo[0].ID;
却不能正确得到数值4,tempID得到的是67108864.
经检查67108864是4在高位时的数值大小.
赋值时本来应该是取内存中的四个Bytes:"04 00 00 00"
可是取值时却是用"00 00 00 04" 的方式.
在调试过程中,从peo[0].ID取值是正确的,得到数字4,可程序执行上面赋值语句后:
tempID还是得到的是67108864.
也就是说,在调试器中取值是正确的,汇编后的程序取值却是不正确的.
程序在开始用了的很长一段时间并没有出现这种问题,这个问题是最近才发生的.
真是百思不得其解。还有一点是明确的,程序涉及到网络传输。
可是如果把结构体中的字符数组大小由33改为36,一切正常了!
原理上肯定是结构体的位对齐问题,但为什么以前编译使用没出问题,现在编译才发生呢?
应该怎么解决呢?
二。寻找问题的原因。
经过CSDN社区各位老大的帮助,并且自己仔细去了解程序中的编译条件部分,原则上理解了这问题的本质所在。
发现在网络模块中使用到了"#pram pack(1)"这样的编译条件,而其它模块则没有加入这种编译条件。
而CSDN中其中一个大虾是这样解释的:“对齐方式是给编译器看的,编译器根据这个来决定内存布局。一旦编译成二进制文件内存布局就已经确定了,如果两段代码对同一个结构使用的对齐方式不同,那么就会对内存里的值做出了不同的解释,赋值的一方认为char[33]占了36个字节,从第37个字节填写04 00 00 00,可是读取的一方认为char[33]只有33个字节,那就从第34个字节处取四个字节当作ID。”
这次网友的解释,我认为指出了问题的本质所在:“如果两段代码对同一个结构使用的对齐方式不同,那么就会对内存里的值做出了不同的解释”。我们的程序给结构体初始化部分是按VC编译器中默认的结构体8字节对齐,而在网络模块中,由于使用了"#pram pack(1)",结果从结构体中取值时,编译器认为结构体是按1字节对齐,最终导致了问题的产生。
三。解决方法
为了解决这个问题,就需要程序中所有的代码对同一个结构体都使用同一种对齐即可。
会有两种解决办法:
一是把网络模块中的"#pram pack(1)"去掉,结构体都是按VC编译器中默认的结构体8字节方式对齐。
二是设置结构体按1字节方式进行对齐,程序所有模块都按这种对齐方式编译。
设置在VC的"project"->"setting..."->"c/c++":struct member aligment改成1 Bytes.