结构体在内存中的存储(内存对齐)

Posted 两片空白

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了结构体在内存中的存储(内存对齐)相关的知识,希望对你有一定的参考价值。

一.内存对齐是什么

首先看例子:

#include<stdio.h>
struct stu{
	char a;
	int b;
	char c;
};
int main(){
	printf("%d\\n", sizeof(struct stu ));
	return 0;
}

输出:
在这里插入图片描述

按照正常的思维,上述结构体中,两个char类型占一字节,一个int类型占四字节,一共应该是6字节,那为什么输出是12字节呢。

再看一个代码。

#include<stdio.h>
struct stu{
	char a;
	char c;
	int b;
};
int main(){
	printf("%d\\n", sizeof(struct stu ));
	return 0;
}

输出:
在这里插入图片描述

上面代码与第一个代码相同,只是结构体中将char类型与int类型交换了一下位置,输出就变成了8字节。

通过上面两个代码发现,结构体在内存中的不是简单的按字节累加的,是存在一定规律储存再内存中的。这个规律就是内存对齐。

我们知道,数据都是保存在内存中的,而计算是在CPU内进行的,在计算是CPU要将数据从内存拿到CPU里,但是,CPU在访存时,不是直接从地址中那数据的,CPU必须遵守某些硬件规则进行访存。

下面举个例子:
在这里插入图片描述
如上如果结构体是这样存储的,要取出数据是很繁琐的。
在这里插入图片描述
如果像上面这样存储,CPU只要直接访存地址就好了,效率就会提高很多。这样存储的方式就是内存对齐。
但是CPU访存的规则是按着计算硬件来的,是不一样的。上面只是举个例子。

总结:通过牺牲空间,换取效率提升保存数据的方案叫做内存对齐。
那为什么要内存对齐呢?如果不内存对齐,因为硬件平台限制,导致访存次数增加,进而会降低效率。

二.怎样内存对齐

首先要掌握结构体对齐的规则:

  1. 第一个成员在与结构体变量偏移量为0的地址处。

    偏移量:结构体成员前面的字节数。
    第一个结构体成员前偏移量为0  所以在保存时不需要对齐。
    
  2. 其它成员变量要对齐到对齐数的整数倍地址处。

    对齐:怎么对齐呢?偏移量能够整数对齐数。不能整除补字节数直到能整除。
    对齐数:对齐数=编译器默认对齐数与该成员大小的较小值。一般编译器没有对齐数,对齐数一般就是成员大小。
    
  3. 结构体总大小为最大对齐数(每一个成员包括第一个成员)的整数倍。

    最大对齐数为所有成员中对齐数的最大值。
    结构体大小计算完后还需要看是否能整除最大对齐数,不能整除补字节数。
    
  4. 如果嵌套结构体的情况,嵌套的结构体对齐到自己最大对齐数的整数倍处,结构体的整体大小就是最大对齐数(含嵌套结构体的对齐数)的整数倍。

    先算出嵌套的结构体的最大对齐数,为外层结构体的对齐数,嵌套结构体的总大小为放在外层结构体的大小。
    
#include<stdio.h>
struct s1{
	double a;//偏移量为 0 对齐数为8  放8个字节
	char b;//偏移量为8 对齐数为1 可以整除 放1个字节
	int c;//偏移量为9 对齐数为4 不能整除 补3个字节 放4个字节
};
//s1 最大对齐数为8  一共8+1+3+4=16可以整除8 于是结构体大小为8  
struct s2{
	char d; //偏移量为 0 对齐数为1  放1个字节
	struct s1 s;//偏移量为1 对齐数为s1的最大对齐数8  不能整除 补7个字节 放结构体大小的字节16个
	int e;//偏移量为24 对齐数为4 可以整除 放4个字节
};
//s2 最大对齐数为8  一共1+7+16+4=28 不能整除8 补4字节 一共为32字节。

int main(){
	printf("%d\\n", sizeof(struct s1 ));
	printf("%d\\n", sizeof(struct s2));
	return 0;
}

输出:
在这里插入图片描述
注意当嵌套数组时:由于数组里面元素类型相同,只需要第一个元素对齐就好了。

#include<stdio.h>
struct stu{
	char a;
	int c[5];//对齐数4 大小20
	char b;
};
int main(){
	printf("%d\\n", sizeof(struct stu ));
	return 0;
}

输出:
在这里插入图片描述

三.修改默认对齐数

用#pragma pack()指令可以修改默认对齐数。
#include<stdio.h>
#pragma pack(2)//设置默认对齐数为2
//对齐数为成员大小与系统默认对齐数的较小值。对齐数为2
struct s1{
	double a;//偏移量为 0 对齐数为2  放8个字节
	char b;//偏移量为8 对齐数为1 可以整除 放1个字节
	int c;//偏移量为9 对齐数为2 不能整除 补1个字节 放4个字节
};
//一共8+1+1+4=14  最大对齐数为2  可以整数 于是结构体大小为14
int main(){
	printf("%d\\n", sizeof(struct s1));
	return 0;
}
#pragma pack()里面不加数字为:取消设置的默认对齐数。

以上是关于结构体在内存中的存储(内存对齐)的主要内容,如果未能解决你的问题,请参考以下文章

结构体在内存中的对齐规则

Visual Studio2008 C++结构体成员需要内存对齐吗?

结构体在内存中的存储方式(转)

C语言中结构体在内存中的存储方式?

Swift中结构体的方法调度&内存分区

结构体内存对齐