C++ 内存对齐

c++ 内存对齐

写出一个struct,然后sizeof,你会不会经常对结果感到奇怪?sizeof的结果往往都比你声明的变量总长度要大,

这是怎么回事呢? 讲讲字节对齐吧.

前言

  • 计算机的内存都是以字节为单位来划分的,CPU一般都是通过地址总线来访问内存的,一次能处理几个字节,就命令地址总线去访问几个字节,

    • 32位的CPU一次能处理4个字节,就命令地址总线一次读取4个字节,读少了浪费主频,读多了也处理不了。
    • 64位的CPU一般读取8个字节。
  • 例如一个 int 类型的数据,如果地址为 8,那么很好办,对编号为 8 的内存寻址一次就可以。如果编号为 10,就比较麻烦,CPU需要先对编号为 8 的内存寻址,读取4个字节,得到该数据的前半部分,然后再对编号为 12 的内存寻址,读取4个字节,得到该数据的后半部分,再将这两部分拼接起来,才能取得数据的值。将一个数据尽量放在一个步长之内,避免跨步长存储,这称为内存对齐。在32位编译模式下,默认以4字节对齐;在64位编译模式下,默认以8字节对齐。

  • 所以说内存对齐就是 时间 & 空间 的一种博弈

规则

  1. 数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从 该成员大小 的整数倍开始(比如int在 32位机为4字节,则要从4的整数倍地址开始存储。

  2. 结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储.(struct a里存有struct b,b里有char,int ,double等元素,那b应该从8的整数倍开始存储.)

  3. 收尾工作:结构体的总大小,也就是sizeof的结果,.必须是其内部最大成员的整数倍.不足的要补齐.

demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
typedef struct bb
{
int id; //[0]....[3]
double weight; //[8].....[15]      原则1
float height; //[16]..[19],总长要为8的整数倍,补齐[20]...[23]     原则3
}BB;

typedef struct aa
{
char name[2]; //[0],[1]
int id; //[4]...[7]          原则1

double score; //[8]....[15]    
short grade; //[16],[17]        
BB b; //[24]......[47]          原则2
}AA;

int main()
{
AA a;
cout<<sizeof(a)<<" "<<sizeof(BB)<<endl; // 48 24
return 0;
}

#pragma pack().

  • 在代码前加一句#pragma pack(1),你会很高兴的发现,上面的代码输出为

    1
    32 16
  • bb是4+8+4=16,aa是2+4+8+2+16=32;

  • 这不是理想中的没有内存对齐的世界吗.没错,#pragma pack(1),告诉编译器,所有的对齐都按照1的整数倍对齐,换句话说就是没有对齐规则.

一些其他的运算符

  • alignof :该运算符返回指定类型的对齐方式(以字节为单位)