最近调试网络的服务端程序,自己写了一个小客户端程序来测试,发现服务程序解包错误。
经调试发现客户端的协议头大小和服务器端的协议头大 小不一致。
原因是服务器端加了#pragma pack(1)
,而客户端没加。
之前没接触过这个编译宏,现在来认真学习之。首先google之~~
原来#pragma pack有几种形式,我所接触到的是#pragma pack(n)
,即变量以n字节对齐。
变量对齐在每个系统中是不一样的,默认的对齐方式能有效的提高cpu取指取数的速度,但是可能会浪费一定的空间。
在网络程序中采用#pragma pack(1)
,即变量紧缩,不但可以减少网络流量,还可以兼容各种系统,不会因为系统对齐方式不同而导致解包错误。
了解了概念和优点,现在我们就来测试之~
平台:CPU—Pentium E5700 内存—2G
操作系统:
- ubuntu 11.04 32bit 编译器:G++ 4.5.2
- windows xp 编译器:VS2010
先看第一个测试:结构体在正常情况和紧缩情况在以上不同环境下占用的内存大小。
struct pack {
int i;
short s;
double d;
char c;
short f;
}
测试结果为:
1:
2:
测试结果分析:
可以看出紧缩后结构体的大小为15,是结构体内置类型大小的和。 但是在默认情况下,结构体的大小都是对齐字节数的倍数。 ubuntu下pack只需要20个字节,而windows要24个字节。 这是因为ubuntu是以4字节对齐,而windows则是以最大的内置类型的字节数对齐,在结构体内最大的内置类型为double,其大小为8个字节。 他们在内存中的对齐方式如下图:
1:
2:
还需注意的是,在对齐类型的内部都是以2字节对齐的。
结论:在默认情况下,linux操作系统是以4字节对齐,windows操作系统则是以最大的内置类型对齐。
第二个测试
一个结构体内包含另外一个结构体,其大小的情况。
内部的结构体为:
struct pack {
short s;
double d;
}
外部的结构体为
struct complex _pack{
char c;
struct pack s;
double d;
}
我们有四种情况: 1. pack紧缩,complex _pack紧缩 2. pack紧缩,complex _pack默认 3. pack默认,complex _pack紧缩 4. pack默认,complex _pack默认
以下的排列均按此顺序。测试的结果:
1:
2:
测试结果分析:
在两个操作系统下,除了第一种情况—-内结构体和外结构体都紧缩—-相同之外,其他三种情况都不相同。 我们可以根据偏移画出结构体在内存中的情况。第一种情况省略。
1:
2:
结论:#pragma pack只影响当前结构体的变量的对齐情况,并不会影响结构体内部的结构体变量的排列情况。 或者说#pragma pack的作用域只是一层。 我们由第三种情况,内部结构体正常,外部结构体紧缩,可以得出结构体的对齐是按偏移计算的。
这里还有一个问题没解决,为什么第二种情况内部结构体的偏移都是1?不是4或者8?