内存字节对齐

定义一个结构体的一般形式为:

struct 结构体名
{
//类型说明符 成员名;
};
例如有如下结构体:

struct Stu
{
int id;
char sex;
float hight;
};
那么一个这样的结构体变量占多大内存呢?也就是

cout<<sizeof(Stu)<<endl; 会输出什么?

在了解字节对齐方式之前想当然的会以为:sizeof(Stu) = sizeof(int)+sizeof(char)+sizeof(float) = 9.

然而事实并非如此!

字节对齐原则
在系统默认的对齐方式下:每个成员相对于这个结构体变量地址的偏移量正好是 该成员类型所占字节的整数倍,且最终占用字节数为 成员类型中最大占用字节数的整数倍

在这个例子中,id的偏移量为0(0=4*0),sex的偏移量为4(4=1*4),hight的偏移量为8(8=2*4),此时占用12字节,也同时满足12=3*4.所以sizeof(Stu)=12.

(1)出现继承关系时:

复制代码
struct A{
int a;
char b;
};
struct B:A{
char c;
int d;
long long e;
};
复制代码
  基类的成员总是在派生类的前面。而且即使有字节对齐,基类对齐后派生类的成员不会占用基类填充的字节,即计算好基类所占字节数后,这些字节只能由基类拥有,不能被派生类的成员占用(即char b后面有3字节的填充,之后才有char c)在派生类中成员的分布只需满足每个变量起始字节序号为该类型所占字节数的整数倍且最终大小为占用字节数最大的类型对应的字节数的整数倍。排列如下:

/*
0 4 8 12 16 24
| | | | | |
aaaab---c---ddddeeeeeeee
*/
(2)出现关联关系时:

  必须满足:1.结构体/类与结构体/类之间不会共用自动补齐的内存,即一个结构体变量/对象对齐之后填补的内存不允许被其他变量/对象占用;2.结构体的起始字节位置必须是该结构体中所占字节数最大的变量的字节数的整数倍;3.最终所占字节数必须是最大所占字节数最大的变量的字节数的整数倍。

(3)出现强制对齐方式时:

  当然,有时候考虑到其他特殊用途,使用#pragma pack(n)来设定以n字节对齐的方式(n可取2的较小次幂,即1,2,4,8,具体取值范围以及默认值与所使用的编译器有关。笔者所测试的环境下vs/vc默认为8,gcc默认为4)。

n字节对齐就是说变量存放的起始地址的偏移量有两种情况:

第一、如果n大于等于该变量所占用的字节数,那么偏移量必须满足默认的对齐方式;

第二、如果n小于该变量的类型所占用的字节数,那么偏移量为n的倍数,不用满足默认的对齐方式。

结构的总大小也有个约束条件,分下面两种情况:如果n大于所有成员变量类型所占用的字节数,那么结构的总大小必须为占用空间最大的变量占用的空间数的倍数;否则必须为n的倍数。

比如有以下代码:

复制代码
1 #pragma pack(push) //保存对齐状态
2 #pragma pack(4)//设定为4字节对齐
3 struct Test
4 {
5 char c1;
6 double d;
7 int i;
8 char c2;
9 };
10 #pragma pack(pop)//恢复对齐状态
复制代码
这时候编译器就会被迫使用我们约定的字节对齐方式,即4字节对齐,因此c1占4字节,d占8字节,i占4字节,c2占4字节,共20字节;

如果我们没有设置字节对齐方式,仍然使用默认对齐的话,这里sizeof(Test) = 24。

注:以上测试效果及完整代码见github.



最后一个问题, 为什么要进行字节对齐

  《Windows核心编程》里这样说:当CPU访问正确对齐的数据时,它的运行效率最高,当数据大小的数据模数的内存地址是0时,数据是对齐的。例如:WORD值应该是总是从被2除尽的地址开始,而DWORD值应该总是从被4除尽的地址开始,数据对齐不是内存结构的一部分,而是CPU结构的一部分。当CPU试图读取的数值没有正确的对齐时,CPU可以执行两种操作之一:产生一个异常条件;执行多次对齐的内存访问,以便读取完整的未对齐数据,若多次执行内存访问,应用程序的运行速度就会慢。



补充:C++类中的数据成员也遵循以上对齐原则,也就是说:

1.在不考虑(或者说在没有)虚函数和虚继承的情况下,sizeof(自定义类)也按照类似上面的方式来计算。

2.如果一个类拥有虚函数或者虚继承,则在数据成员的基础上相当于多一个指针类型的数据成员(位置在所有数据成员的前面),最后计算时加上即可。

3.如果一个类或者结构体不含有任何数据成员,且无虚函数以及虚继承,则sizeof()结果为1。

4.静态成员不在计算范围。

test实例:

struct A {
char a;
char b;
char c;
};

struct B {
int a;
char b;
short c;
};

struct C {
char b;
int a;
short c;
};

#pragma pack(2)
struct D {
char b;
int a;
short c;
};

占用的空间分别是

A:对齐值为:1 。大小为:3
B:对齐值为:4 。 大小为:4+4 = 8(第一个4为int,第二个4为char 和 short ,要空余1个)
C:对齐值为:4。大小为:4+4+4 = 12(第一个为char ,空余3个,第二个为int ,第三个为char 空余3个)
D:指定对齐值为:2(使用了#pragma pack(2)) 。大小为2+4+2 = 8。(第一个char,空余1个,第二个为int ,4个,第3个位char,空余1个)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在C++中,结构体都是由多个成员变量组成的。为了在内存中高效地存储这些成员变量,编译器会对结构体进行内存字节对齐内存字节对齐是指将结构体中的成员变量按照一定的规则排列,使得每个成员变量的内存地址都是其长度的整数倍。这样一来,访问这些成员变量时就可以减少内存访问次数,提高访问效率。 内存字节对齐的规则如下: 1. 结构体的起始地址必须是其最宽基本成员的整数倍。 2. 结构体的每个成员变量相对于起始地址的偏移量必须是其型大小的整数倍。 3. 结构体的总大小必须是其最宽基本成员大小的整数倍。 例如,一个结构体中有两个成员变量,一个是int型,一个是char型。如果按照默认的字节对齐规则排列,结构体内存布局如下: ``` struct MyStruct { int a; char b; }; // 内存布局 // +---+---+ // | a | b | // +---+---+ ``` 这里,int型占用4个字节,char型占用1个字节。因此,编译器会按照4字节对齐的方式排列结构体。由于int型是最宽的基本型,所以结构体的起始地址必须是4的倍数,而char型则放在了4字节边界上。 需要注意的是,内存字节对齐规则可能会因为编译器的不同而产生变化。有些编译器允许开发者通过预处理指令来指定结构体的对齐方式,例如: ``` struct MyStruct { int a; char b; } __attribute__((aligned(8)))); ``` 这里,`__attribute__((aligned(8)))`表示MyStruct结构体需要以8字节对齐的方式排列。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值