理解字节对齐问题--表格法

探究结构体的对齐问题,使得我们更好的理解结构体在内存中的布局。


自定义字节对齐数值时,内存偏移计算

默认情况下编译器会对结构体进行字节对齐,提高访问速度。形如下面的例子允许自定义“字节对齐数值”(这个词不一定准确哈):

#pragma pack(4) //按4字节对齐
typedef struct _Product
{
   int nId;
   string strName;
   double dPrice;
   char chFlag;
}Product;
#pragma pack()

需要强调的是字节对齐跟结构体中各元素的顺序有关(下面会有例子说明)。

下面用一个表格来描述上述结构体的内存分布问题。sizeof(string)在VS2013下是28;在MinGW 5.3.0 32bit是24。

成员长度min(自身长度,字节对齐数值)当前地址是否是min的倍数实际起始地址实际地址范围当前地址
nId4min(4,4)=400-34
strName28min(28,4)=41倍,是44-3132
dPrice8min(8,4)=48倍,是3232-3940
chFlag1min(1,4)=140倍,是404041
Productmin(A,4)=4否,41,42,43补444444

可得,sizeof(Product) = 44(最大元素的长度的倍数)。


采用系统默认对齐方式,内存偏移计算

好,下面咱们不自定义对齐数值,采用编译器默认:

typedef struct _Product
{
   int nId;
   string strName;
   double dPrice;
   char chFlag;
}Product;

下面用一个表格来描述上述结构体的内存分布问题。sizeof(string)在VS2013下是28;在MinGW 5.3.0 32bit是24。

成员长度min(自身长度,字节对齐数值)当前地址是否是min的倍数实际起始地址实际地址范围当前地址
nId4min(4,4)=400-34
strName28min(28,4)=41倍,是44-3132
dPrice8min(8,8)=84倍,是3232-3940
chFlag1min(1,1)=140倍,是404041
Productmin(A,8)=8否,42-47补484848

可得,sizeof(Product) = 48(最大元素的长度的倍数)。
从上面的情况可知,字节对齐数值按每一个子项元素类型的自然边界对齐(natural alignment),也就是说自然边界对齐即为默认对齐方式。


结构体字节对齐计算方式,跟各元素的顺序有关

下面再举一例来强化一下。

typedef struct _Product
{
    int nId;
    double dPrice;
    string strName;
    char chFlag;
}Product;

下面用一个表格来描述上述结构体的内存分布问题。sizeof(string)在VS2013下是28;在MinGW 5.3.0 32bit是24。

成员长度min(自身长度,字节对齐数值)当前地址是否是min的倍数实际起始地址实际地址范围当前地址
nId4min(4,4)=400-34
dPrice8min(8,8)=8否,4-7补88-1516
strName28min(28,4)=44倍,是1616-4344
chFlag1min(1,1)=144倍,是444445
Productmin(A,8)=8否,45-47补484848

可得,sizeof(Product) = 48(最大元素的长度的倍数)。顺序不同,sizeof的值可能不同。

结构体如果不指定字节对齐数值,那么将小字节元素放在前面,将会更加节约内存。


关于结构体char元素的地址输出:

std::cout<<这个操作符对char*是有重载的
所以如下语句

std::cout<<Product.chFlag<<&(Product.chFlag)<<endl;

都会输出该项的值,而不会输出其地址,所以这里要换成printf.


关于对象或容器作为结构体的元素

测试得知,无论Product::strName赋值多长,该字符串有多少个字符,其在结构体内部的地址偏移计算,始终是按照28个字节(MinGW32)来计算的。也就是是strName的字符串内容实际上并不在结构体的内存空间里,所以序列化要格外注意。


如下宏定义可以获取已知结构体的相关值

//各个元素的sizeof值
#define StructFieldSize(type, field) sizeof(((type*)0)->field)

//各个元素的偏移量
#define StructOffset(type, member) ((size_t)&((type*)0)->member)

//根据已经分配内存的的结构体对象,的某一个元素的地址获取结构体首地址
#define GetStructHeaderPoint(ptr, type, member)((type *)((char*)(ptr)-(unsigned long)(&((type *)0)->member)))
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值