在c、c++笔试中,内存对齐问题经常考,尽管这样,这几年来我一直忽略了这个问题,记忆中一直是大学课本中struct与union的区别,那时候也不曾有提及过内存对齐,现在想起来,真是非常大的漏洞。而且很多笔试想当然的觉得struct的大小就是内部所有变量大小的和,却不知这种错误的答案让多少面试官默默的鄙视,直到前一次面试,面试官讽刺到:“作为一个c++程序员,竟然不知道内存对齐这样的基础知识。。。”,深感愧疚,我对不起这些年与c++的朝夕相处啊!回来后查了一些文章,又做了几个测试,才搞明白其中的原理,现记录一下,方便有同样经历的猿类自查。
现举个例子:
typedef struct{
char a;
int b;
}node;
cout<<sizeof(node)<<endl;
以前, 我一直以为输出的结果是5(1 + 4) 直到。。。今天它怎么变成了8!!!
分析一下,经过网上查找资料,两个原因:
1、平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2、性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
原则:
每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数)。程序员可以通过预编译命令#pragma pack(n),n=1,2,4,8,16来改变这一系数,其中的n就是你要指定的“对齐系数”。
对齐步骤:
1、数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的对齐按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。
2、结构(或联合)的整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。
3、结合1、2颗推断:当#pragma pack的n值等于或超过所有数据成员长度的时候,这个n值的大小将不产生任何效果。
备注:数组成员按长度按数组类型长度计算,如char t[9],在第1步中数据自身长度按1算,累加结构体时长度为9;第2步中,找最大数据长度时,如果结构体T有复杂类型成员A的,该A成员的长度为该复杂类型成员A的最大成员长度。
废话说了这么多,我觉得还是使用言简意赅的画图诠释一下。
结构体 node
a | null | null | null |
b | b | b | b |
意思是char占了4个字节,第一个字节内存值,后面三个没有用到,int使用了四个字节
再看一个例子
typedef struct{
char a;
short b;
int c;
char d;
}node;
cout<<sizeof(node)<<endl;
输出的结果应该是12,不是8,用表格的形式表示一下,如下:
a | b | b | null |
c | c | c | c |
d | null | null | null |
我觉得规律是,结构体的大小一定是内部最大类型大小的整数倍,因为边界以最大类型大小确定的。
以后不会再在这里翻船了!铭记!