网络VC字节对齐

            我们知道的当一个结构体,如果我们想知道他占多少内存空间的话我们可以利用sizeof()来查看,但是有时候sizeof()得出的结构竟然和我们想象的不一样,这和编译器有很大的关系。编译器有自己的字节对齐机制,他为了某些原因,在内存上进行了一些调整。和填充,导致得到了与我们想法不一样的结果。虽然vc字节对齐网上有很多资料,但是呢,下面我就说说自己的理解,自己对这套规则的记忆方法。


采用字节对齐的原因:
         各种平台在内存处理上有很大不同,存取方式也差别很大。很多平台为了提高自己数据的存取效率,都会充内存上对数据进行一些调整,如数据存放进行对齐,以便提高自己生的数据存取速度,提高程序执行效率。

字节对齐规则:

1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除; 
2) 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding); 
3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(trailing padding)。

实例说明:

假如我们定义了一个结构体 

typedef	struct tagHeader{
			int nLen;
			char type;
			short one;
		}Header,*pHeader;

按照我们正常的思路  sizeof(Header) = 4(int)+1(char)+2(short ) 也就是7个字节  但是事实上呢,你如果运行一下你会发现

sizeof(Header) = 8 为什么会这样呢?原因就是上面所说的字节对齐造成的,那么现在我们就按照上面所说的字节对齐的规则来,看一下他到底是 怎么进行对齐的

1.首先我们来 找出 在这个结构体里面最大的基本成员变量空间是int 他占4个字节.

2.然后我们按照结构体内部变量定义顺序(既是 int-char-short)来分析

(1)int nlen 相对于结构体的起始地址0, 他是4的整数倍,满足字节对齐的条件2

(2)第二个变量char type 相对于结构体的其实地址是4,他是1的整数倍,显然也满足字节对齐规则的条件2

(3)第三个变量 short one 相对于结构体的起始地址是5(0+4+1) 显然这不是2的整数倍,不满足对齐规则条件2,于是这时候就要采取字节对齐了,系统为了让他满足字节对齐,会自动填充一个字节使其的其实地址变为6 ,这样便可以整除 2 了,所以one的起始地址就变为了6 这时就满足对齐规则条件2.

3.最后我们来看看 结构体总共占的大小 4(int)+1(char)+1(系统自动填充)+2(short) =8  而8恰好是4(该结构体最大的基本类型是int)的倍数 满足条件3,因此该结构体真正的大小就为8.

内存结构如下图一

下面我们再来看一个例子,我们仅仅把结构体内容调换一下

typedef	struct tagHeader{
			char type;
			int nLen;
			short one;
		}Header,*pHeader;
同上面一个例子一样的分析方法

1.首先找出 在这个结构体里面最大的 基本成员变量是int 他占4个字节.

2.然后我们按照结构体内部变量定义顺序(既是 char-int-short)来分析

          (1)第一个变量char type 相对于结构体的起始地址0, 他的起始地址是1的整数倍,满足字节对齐的条件2

          (2)第二个变量 int nlen相对于结构体的起始地址是1,他的起始地址不是4的整数倍,不满足字节对齐的条件2,因此要进行字节填充。为了成为4的倍数,所以要填充3个字节。

(3)第三个变量 short one 相对于结构体的起始地址是8(0+1+3+4) 是2的整数倍.满足字节对齐的条件2。

3.最后我们来看看 结构体总共占的大小 1(char)+3(系统自动填充)+4(int)+2(short) =10  而10不是4(该结构体最大的基本类型是int)的倍数 不满足满足条件3,因此必须再次进行填充,使整个结构体的大小满足4的倍数,因此还要填充2个字节 故 最终字节应该是12.

   内存结够如下图二

最后我们再来看一个复合的结构体

typedef	struct tagHeader{
			char type;
			int nLen;
			short one;
		}Header,*pHeader;

typedef struct tagTest{
	char type;
	Header head;
}TEST,*LPTEST;

仍然按照我们自己的步骤来 分析

1.首先找出 在这个结构体里面最大的 基本类型成员变量是int 他占4个字节(Header 不是基本类型).

2.然后我们按照结构体内部变量定义顺序(既是 char-header)来分析

          (1)第一个变量char type 相对于结构体的起始地址0, 他的起始地址是1的整数倍,满足字节对齐的条件2

          (2)第二个变量 Header相对于结构体的起始地址是1,他的起始地址不是4的整数倍(这里是最大基本类型成员变量是int),不满足字节对齐的条件2,因此要进行字节填充。为了成为4的倍数,所以要填充3个字节。

(3)按照我们分析的前面分析的方法分析结构体的head的内存情况。

3.最后我们来看看 结构体总共占的大小 1(char)+3(系统自动填充)+10(结构体) =14  而14不是4(该结构体最大的基本类型是int)的倍数 不满足满足条件3,因此必须再次进行填充,使整个结构体的大小满足4的倍数,因此还要填充2个字节 故 最终字节应该是16.

内存结构如下图三

                                                                              内存结构图解

以上便是字节对齐的情况。

字节对齐是提高了数据存取熟读,提高程序的效率,但是有些时候我们的程序不需要字节对齐。比如我们要将一个结构体发送出去,发送到网络中的另一台计算机上,但是这台计算机采用的平台和我们开发的平台却不一样。假若我让系统字节对齐的话,但接受方由于平台和我们不一样,这样很可能就导致对方解析数据不正确,所以有时候不能使用字节对齐。可那么假如我不想让系统字节对齐,或者说我们想自己指定对齐字节是多少,那我们该怎么办?

C++便可以这样 采用#pragma pack 命令, 当你定义结构体时这样定义。

#pragma pack(push,1) //表示已1字节对齐
typedef	struct tagHeader{
			char type;
			int nLen;
			short one;
		}Header,*pHeader;
#pragma pack(pop)
或者这样也行
#pragma pack(push)
#pragma pack(1)//表示以一字节对齐
typedef	struct tagHeader{
			char type;
			int nLen;
			short one;
		}Header,*pHeader;
#pragma pack(pop)

#pragma pack(push)和#pragam pack(pop) 之间的结构体就采用一字节对齐,如果你想以其他字节对齐,自己修改#pragma中的数字便是,如以4字节对齐 就改为

#pragma pack(4) 这样便可以了。


  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值