今天,Mayuyu意外地写了如下两段代码:
片段一:
#include <iostream>
#include <string.h>
#include <stdio.h>
using namespace std;
struct Node
{
char a;
int x;
char b;
};
int main()
{
cout<<sizeof(Node)<<endl;
return 0;
}
片段二:
#include <iostream>
#include <string.h>
#include <stdio.h>
using namespace std;
struct Node
{
char a;
char b;
int x;
};
int main()
{
cout<<sizeof(Node)<<endl;
return 0;
}
可以很容易看出,上面的两段代码只是结构体内的变量的排列顺序不同。输出结果却分别是12和8。
为什么会这样?Mayuyu迷惑了,根据Mayuyu从C语言中学到的一个char在内存中占1字节,一个int占4字节。那
么sizeof(Node)应该是6字节才对啊。为什么变量排列顺序不同,会导致结构体在内存中所占总空间不同? 这一
切要从C语言中内存对齐说起。
那么,接下来你就该问,什么是内存对齐? 接下来Mayuyu将一一给你讲述。
我们知道,内存是一个连续的块,它是以n个字节对齐一个单位的,一般默认情况下n = 4,除非你自己重新定义对
齐单位的字节数。如下图:
在C语言或者C++中,都会遵循内存对齐。比如对于片段一的代码来说,先是char a占用了最上边的一格,然后
int x申请4字节内存,但是发现在上面的一个单位也就是4字节里面还剩余3字节,已经小于4字节,那么这3字
节不会被申请,而会申请下一个单位,刚好4字节,满足条件,继续char b也会占一个字节,但是最小的单位是
4字节,那么剩下的3字节也算在内,如下图:
所以,这样下来就必定是12字节,我们也就不会奇怪会输出12了,同样的道理我们也会得到片段二的结果为8。
也就是说最小的分配单位就是4字节,当然这里的“4”实际上是可以自己指定的,只是系统默认是4而已。比如我们
完全可以自己指定一个单位的大小。比如指定为一个单位的大小为1字节。如下:
#include <iostream>
#include <string.h>
#include <stdio.h>
using namespace std;
#pragma pack(push);
#pragma pack(1);
struct Node
{
char a;
int x;
char b;
};
#pragma pack(pop);
int main()
{
cout<<sizeof(Node)<<endl;
return 0;
}
这样我们就可以输出我们真正期望的值6。
讲到了这里,你或许会问C语言中内存对齐的单位为4字节,这样做岂不是很浪费。比如片段一,本来6字节就够了,
但是我们却足足申请了12字节,大大地浪费啊。那么Mayuyu将会告诉你,你说得没错,但是你忽略了另一个因素,
那就是效率。
假如我们不进行内存对齐,把内存申请后,在取数的时候CPU会非常痛苦。因为如果一个数跨越两个内存单位,我们
要取出它,CPU会访问两次内存。对于片段一的代码如果不进行内存对齐就是这样,取出x的值CPU就会访问两次内
存,如果进行内存对齐,只需访问一次。
实际上,内存对齐的原因主要有以下两个:
(1)平台原因:并不是所有的硬件平台都能访问任意地址的任意数据。
(2)性能原因:就是上面所说的,减少CPU的访问内存次数,性能也就会相应提高。
从上面可以看出,我们可以指定内存对齐一个单位的字节数,而这里的字节数一般都是取1,2,4,8,16等等。一般系
统默认是4字节为一个单位,因为这样可以把效率和内存使用情况折中。