①why——为什么?为什么要这么做?理由何在?原因是什么?
为什么要在C语言中要进行内存对齐呢?如果不对齐会发生事情呢?
个人见解:
在结构体里面保存的是什么呢?是变量的声明,在我们没有进行实例化之前,没在内存中创建栈帧,当使用的时候会创建栈帧以保存变量,那么就必然会去访问变量,那么计算机底层是怎么访问变量的呢?如果计算机是64位机,那么每一次都会访问8个字节,32位机……以此类推。计算机在访问变量的时候会读取出来进行一系列的操作,那么为什么要采取这种内存对其的策略呢?我们先来看一下下方草图:
计算机会一个字一个字的读取,然而想要读取变量i的时候,就出现了两种情况,第一种是没有内存对其的,读取i要2次,第二个是采用了内存对其的,用了一次。可以看见采用了内存对齐的变量更效率
官方解释:
1. 平台原因(移植原因): 不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能
在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2. 性能原因: 数据结构(尤其是栈)应该尽可能地在自然边界上对齐。 原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
②what——是什么?目的是什么?作什么工作?
内存对其的本质关注的是结构体中变量的所占空间大小,以及存储形式。会根据对其规则来进行存储。
对其规则:
1. 第一个成员在与结构体变量偏移量为0的地址处。
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。vs默认的对其数大小为8
3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
接下来让我们根据上面的规则
猜一猜这段代码输出的结果是多少?
typedef struct test
{
char c1; //1
int i; //4
char c2; //1
}test;
int main()
{
printf("sizeof(test) :: %d\n", sizeof(test));
return 0;
}
答案是12.答案是怎么得来的呢?
来梳理一下过程:
1、首先char 占一个字节,int 占4个字节
2、根据规则1,那么偏移数为0的位置就是c1,此时一个字节占用,当前位置0,下一个位置1;
3、根据规则2,可以算出对其数为Min(4, 8) = 4,因此i 的起始偏移位置为4,那么中间就必然空着3个位置,当前位置7, 下一个位置8;
4、根据规则2,可以算出其对其数为1,8是1的整数倍,因此c2的偏移位置为8,当前位置8,下一个位置9;
5、变量已经全部存储,根据规则3,先求出最大所有成员的最大对其数MAX(1, 4) = 4; 但是此时的位置正在8这个位置,共占用9个字节,不满足条件3,改变位置,增加占用字节,偏移位置调整至11,至此,结构体总大小运算结束,占用12个字节,下图为具体存储形式,看到这里相比大家和我一样想到如何优化这个结构体的变量了吧,那就是尽量合理利用每一个变量的位置。
上面的运算步骤大家都已经看了,那么接下来我们一起看这段代码会占用多少字节空间呢?
typedef struct test
{
double d;
char c1;
int i;
char c2;
}test;
int main()
{
printf("sizeof(test) :: %d\n", sizeof(test));
return 0;
}
答案是24,如果算错的小伙伴一定要画图检验以下!!!
但是到现在我们还没有看到规则4是如何运用的,那么接下来让我们看下一段代码
大家也来猜猜结果是多少吧?
typedef struct test
{
double d;
char c1;
int i;
char c2;
}test;
struct test1
{
double d2;
test t1;
int i;
};
答案是40,那么这里是怎么得来的呢?
1、根据规则1,此时偏移位置从0开始,当前位置7,下一个位置8;
2、该结构体的大小为24,根据规则4,test结构体中的最大对其数为8(double),因此此处的首位置要为8的整数倍,目前刚刚好,直接从偏移位置8处开始存储,当前位置31,下一个位置32;
按照上面的规则来计算就可以得出答案40。
到这里呢,本期的结构体大小计算就接近尾声了,也感谢花时间看我的文章!!!
内容若有差错,恳请指正!