C 语言

关注公众号 jb51net

关闭
首页 > 软件编程 > C 语言 > C语言 内存对齐

C语言结构体中内存对齐的问题理解

作者:诚挚的乔治

内存对齐”应该是编译器的“管辖范围”。编译器为程序中的每个“数据单元”安排在适当的位置上。但是C语言的一个特点就是太灵活,太强大,它允许你干预“内存对齐”。如果你想了解更加底层的秘密,“内存对齐”对你就不应该再模糊了

前言

学C的同学应该知道~

想精通C语言就不得不面对—指针与内存

续上次指针的进阶,这一章我来聊一聊C语言内存对齐的问题

学习结构体的你有没有注意过结构体向系统申请的内存为多少呢的😁

思考

#include<stdio.h>
typedef struct s1
{
	char a;
	char b;
	int c;
}s1;
typedef struct s2
{
	char a;
	int c;
	char b;
}s2;
int main()
{
 
	//内存对齐的现象
	printf("%d\n", sizeof(s1));
	printf("%d\n", sizeof(s2));
	return 0;
}

很显然这一段代码就是计算s1与s2向系统申请的内存大小

我:两个char类型各为一,再加上应该int类型的四,结果就是六

诚挚的乔治:你说的对,但不完全对,在结构体中会出现内存对齐的现象,不信?看结果

别慌,看到文章的最后,你(也许)就会恍然大悟

在结构体中,内存不是成员的大小之和

结构体在内存中开辟空间时内存对齐的规则

1.结构体中的第一个成员存放在这个结构体的零偏移处,故第一个成员char类型的的偏移量为零

2.从第二个成员开始,每个成员都要对齐到成员对齐数的整数倍

(对齐数--成员自身大小与默认对齐数的最小值的整数倍,如果自身大小是四,默认对齐数是八,最终的对齐数就是四的倍数),一般情况下默认对齐数就是八。

3.结构体的总大小必须是最大对齐数的整数倍 。

(最大对齐数就是每个成员对齐数中的最大值)

4.如果结构体中嵌套结构体的情况下,嵌套的结构体就对齐到自己成员对齐数的最大对齐数的整数倍处,结构体的总大小就是最大对齐数(含嵌套的结构体成员)的整数倍。

 下面其中的一个结构体进行分析:

 先向大家介绍一下本章的配角—offsetof

offsetof的返回值就是距离这个结构体(自定义类型)起始位置的值

参数就是结构体的名称和结构体成员的名称。

#include<stddef.h>
#include<stdio.h>
typedef struct s
{
	char a;
	int b;
	char c;
}s;
int main()
{
	//offsetof-是指偏移量
	printf("%d\n", offsetof(s,a));
	printf("%d\n", offsetof(s,b));
	printf("%d\n", offsetof(s,c));
	return 0;
}

char a; //因为char a是结构体第一个成员,所以偏移量就是零

int b;   //自身大小:  4    默认对齐数:8   对齐数:4的倍数即可,所以偏移量就是四

char c; // 自身大小: 1    默认对齐数:8   对齐数:1的倍数即可,所以偏移量就是八

又因为最终的结构体大小是成员最大对齐数的倍数,也就是四的倍数,所以最终的结构体的大小应该就是十二。

到这里是不是有一定的思路了,别急,再来一道试试吧

#include<stddef.h>
#include<stdio.h>
typedef struct s
{
	double a;
	char b;
	int  c;
}s;
int main()
{
	//offsetof-是指偏移量
	printf("%d\n", offsetof(s,a));
	printf("%d\n", offsetof(s,b));
	printf("%d\n", offsetof(s,c));
	printf("%d", sizeof(s));
	return 0;
}

结果如下:

是不是跟你想的一样呢?

同样的,用一样的方法进行解释

double a;     //因为double a是结构体第一个成员,所以偏移量就是零

char b;     //自身大小:1    默认对齐数:8  对齐数:1的倍数即可,所以偏移量就是八

int  c;        //自身大小:4    默认对齐数:8   对齐数:4的倍数即可,所以偏移量就是十二

最终结构体的大小就是四的整数倍十六

下面给应该结构体嵌套结构体的例子

#include<stdio.h>
#include<stddef.h>
typedef struct s1
{
	char i;
	char j;
	int k;
}s1;
typedef struct s2
{
	char a;
	int c;
	s1;
}s2;
int main()
{
 
	printf("%d\n", offsetof(s2, a));
	printf("%d\n", offsetof(s2, c));
	printf("%d", sizeof(s2));
	return 0;
}

我相信此时的你一定会计算结构体想系统申请的大小,以及内存对齐是咋回事

其实,内存对齐也就那么回事儿~

为什么存在内存对齐

知道怎样计算后,你是否和我一样思考

为什么内存的申请不能想main函数中的内存申请一样,要多少就申请相应大小的空间,这样既省内存,也不用考虑这么多。

1.平台的原因

所谓平台原因就是与硬件有关,硬件不能访问内存中的每一个空间,换句话说,就是按一定的规律进行访问,这样内存对齐就起到了很好的作用

2.性能的原因

数据结构(尤其是栈),应该尽可能的在自然边界上对齐

因为我们的CPU访问空间,就是一次性按四个字节的空间来访问,内存对齐在一定的时候避免了访问一次的空间进行了二次访问

如下访问应该int类型内存对齐后只需要访问一次

总的来说内存对齐就是拿空间换取时间

当然,我们可以实现内存对齐之上,也可以进行省一定空间

就想博客开头一道题,一模一样的代码,最终的结构体的大小却不一样,这种就是优化版的结构体

这里给一个小的技巧,就是把空间小的成员尽量放在一起

到此这篇关于C语言结构体中内存对齐的问题理解的文章就介绍到这了,更多相关C语言 内存对齐内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文