一、sizeof运算符
上一篇文章已经讲过sizeof相关的知识,这篇主要是讲一下sizeof计算结构体内存大小的相关内容,以及字节对齐的知识。
首先补充一些sizeof的知识,sizeof运算符有两种写法:①sizeof (type);②sizeof expr。即一个加括号,一个不加括号。对于变量名,指针,引用及对象名等两种写法都正确,但是对于类型名(如int ,char或者自定义类型名)只能使用第一种写法。另外注意sizeof不会将数组名转换为指针,具体内容参考上一篇文章
一些基本类型的sizeof求得值(32位):
char c = 1; //sizeof c 值为:1
short s = 1; //sizeof s 值为:2
int i = 1; //sizeof i 值为:4
unsigned int ui = 1; //sizeof ui 值为:4
long l = 2; //sizeof l 值为:4
long long ll=2; //sizeof ll 值为:8
float f = 1; //sizeof f 值为:4
double d = 1; //sizeof d 值为:8
int *p = 0; //sizeof *p 值为:4
//sizeof p 值为:4(指针本身的大小)
由于指针储存的是地址,因此指针的值就是一个32位的位的二进制,也就是4字节,指针的大小实际上由CPU的寻址位数决定。
二、字节对齐
1、为什么要进行字节对齐
之所以会出现字节对齐,这其实与计算机底层的硬件工作原理有关。首先考虑一下当处理器的存取粒度为1个字节时,对于一个int型变量(4个字节),无论该变量的首地址存储在什么位置,都需要读取4次才能把该变量读取出来(一次1个字节),这种情况下没有字节对齐什么事。
然而处理器的内存存取粒度都是以2字节、4字节、8字节、16字节甚至更多为单位来作为内存的存取粒度,这种情况下,进行字节对齐能够提高处理器的对内存的存取效率。以32位系统存取粒度为4字节的处理器来分析一下。如下图所示:
(上传的图片,不知道如何调整图片大小,尬~)如果int型变量i的首地址为0,那么处理器就可以一次性将数据读取出来,非常完美。但是考虑以下情况:
这种情况下,变量i的首地址为2,那么此时处理器需要先取出地址为0~3的内容,并且去除0和1的内容,然后再一次取出地址为4~7的内容,并且去除地址为6和7的内容,这样才能得到变量i的内容。显而易见这种情况下处理器的存取效率很低。为了避免这种情况的发生,因此才出现了字节对齐这一概念。
2、内存对齐的规则
基于以上的分析,可以看出字节对齐的关键就是控制变量储存地址的首位置。首先介绍两个概念:
①、对齐系数:每个特定平台上的编译器都有自己的默认对齐系数(对齐模数),可以通过预编译命令:#pragma pack(n),n=1,2,4,6,8,16来改变这一系数。(32位系统好像默认n=4,即默认对齐系数为4);
②、有效对齐值:给定的对齐系数(#pragma pack(n)中的n值)与结构体中最长数据类型长度的较小值,也称为对齐单位。比如:结构体中的最长数据类型为double(长度为8字节),默认的对齐系数为4,那么有效对齐值就为4.
了解以上概念之后,只需要记住以下两个重要规则,就能计算结构体的内存大小。
1、结构体中第一个成员的偏移量为0,往后每一个成员相对于结构体首地址的偏移量都是该成员大小与有效对齐值中较小那一个的整数倍,编译器会通过自动填充字节来进行调整;
2、结构体的总大小为有效对齐值的整数倍,编译器会在最后一个成员末尾通过填充字节来进行调整。
接下来通过几个例子来理解以上两个规则:
struct
{
int i;
char c1;
char c2;
} s1;
理论上变量i占4个字节,c1和c2各占1个字节,那么总共占用6个字节,而实际上通过sizeof s1得到的却是8个字节。这就是字节对齐后的结果。实际上结构体s1的内存分配示意图如下所示:
首先看一下规则1:结构体的第一个成员的偏移量为0,因此变量i的首地址为0,第二个成员c1相对于首地址的偏移量应该为1的倍数,放在地址4即满足要求,同理第3成员c2放在地址5也满足规则1关于偏移量的要求。此时结构体s1以及占用了6个字节。由规则2得到,结构体的总大小为有效对齐值(该例子为4)的整数倍,因此编译器会在c2的末尾自动填充两个字节使得总字节数等于8,这就是sizeof s1等于8的由来。
通过以上分析也可以看出,结构体的大小其实与成员的声明顺序有关,如果交换变量i和变量c1的声明顺序,即:
struct
{
char c1;
int i;
char c2;
} s2;
此时通过sizeof s2得到的结果就变成了12,按照上述规则得到此时结构体s2的内存分配示意图如下所示:
具体分配过程就不在一一分析了,反正记住上面两条规则。
OK,结束了。