c语言结构体字节对齐的实现方法
1.什么是字节对齐
在c语言的结构体里面一般会按照某种规则去进行字节对齐。
我们先看一段代码:
struct st1 { char name; double age; char sex; }; //32位下 sizeof(struct st1) = 16 //64位下 sizeof(struct st1) = 24 struct st2 { char a; char b; char c; }; //32位和64位下, sizeof(struct st2)都是3个字节
从以上结果可以看出,结构体st1在32位下是按照4个字节来对齐的,在64位下则是按照8个字节来对齐的,结构体st2则不管32位还是64位则都是按照1个字节对齐的。
那么我们可以总结出对齐规则如下:
- 在所有结构体成员的字节长度都没有超出操作系统基本字节单位(32位操作系统是4,64位操作系统是8)的情况下,按照结构体中字节最大的变量长度来对齐;
- 若结构体中某个变量字节超出操作系统基本字节单位,那么就按照系统字节单位来对齐。
注意:并不是32位就直接按照4个字节对齐,64位按照8个字节对齐。
2.为什么要有字节对齐
首先普及一点小知识,cpu一次能读取多少内存要看数据总线是多少位,如果是16位,则一次只能读取2个字节,如果是32位,则可以读取4个字节,并且cpu不能跨内存区间访问。
假设有这样一个结构体如下:
struct st3 { char a; int b; }; //那么根据我们第1节所说的规则,在32位系统下,它就应该是8个字节的。
假设地址空间是类似下面这样的:
在没有字节对齐的情况下,变量a就是占用了0x00000001这一个字节,而变量b则是占用了0x00000002~0x000000005这四个字节,那么cpu如果想从内存中读取变量b,首先要从变量b的开始地址0x00000002读到0x0000004,然后再读取一次0x00000005这个字节,相当于读一个int,cpu从内存读取了两次。
而如果进行字节对齐的话,变量a还是占用了0x00000001这一个字节,而变量b则是占用了0x00000005~0x00000008这四个字节,那么cpu要读取变量b的话,就直接一次性从0x00000005读到0x00000008,就一次全部读取出来了。
所以说,字节对齐的根本原因其实在于cpu读取内存的效率问题,对齐以后,cpu读取内存的效率会更快。但是这里有个问题,就是对齐的时候0x00000002~0x00000004这三个字节是浪费的,所以字节对齐实际上也有那么点以空间换时间的意思,具体写代码的时候怎么选择,其实是看个人的。
3.手动设置对齐
什么情况下需要手动设置对齐:
- 设计不同CPU下的通信协议,比如两台服务器之间进行网络通信,共用一个结构体时,需要手动设置对齐规则,确保两边结构体长度一直;
- 编写硬件驱动程序时寄存器的结构;
手动设置对齐方式有两种:
代码里添加预编译标识:
//用法如下 #pragma pack(n)//表示它后面的代码都按照n个字节对齐 struct st3 { char a; int b; }; #pragma pack()//取消按照n个字节对齐,是对#pragma pack(n)的一个反向操作 //这里计算sizeof(st3)=5
上面这两行其实就类似于开车的时候,走到某一段路的时候,发现一个限速60公里的指示牌,过了那一段路以后,又会有解除限速60公里的指示牌。
定义结构体时:
//用法如下 struct bbb { char a; int b; }__attribute__((packed));//直接按照实际占用字节来对齐,其实就是相当于按照1个字节对齐了 //这里计算sizeof(st3)=5
4.结构体比较方法
可以使用内存比较函数memcpy进行结构体比较,但因为结构体对齐可能会有填充位不一致的情况,此时需要注意:
- 设置为1个字节对齐,使它没有空位;
- 事先对结构体进行初始化;
memcpy(char *dest, const char* src, int len); //头文件#include<string.h>
上一篇:C++ 中使用lambda代替 unique_ptr 的Deleter的方法
栏 目:C代码
下一篇:C语言实现24位彩色图像二值化
本文标题:c语言结构体字节对齐的实现方法
本文地址:http://www.codeinn.net/misctech/192359.html