前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >结构体中的内存对齐是什么?一起搞懂它

结构体中的内存对齐是什么?一起搞懂它

作者头像
用户11036582
发布2024-03-21 18:38:25
480
发布2024-03-21 18:38:25
举报

给大家分享一句我很喜欢我话: 知不足而奋进,望远山而前行!!! 铁铁们,成功的路上必然是孤独且艰难的,但是我们不可以放弃,远山就在前方,但我们能力仍然不足,所有我们更要奋进前行!!! 今天我们更新了结构体内存对齐的内容,

一、结构体

1.1结构体内存对齐:

首先我们来看一下结构体内存对齐的规则:

1、第一个成员在与结构体变量偏移量为0的地址处; 2、其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处(对齐数=编译器默认的一个对齐数 与 该成员大小的较小值)( vs中默认的值为8); 3、结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍; 4、如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构的对齐数)的整数倍。

1.2结构体内存对齐的计算:

先给你一段代码:

代码语言:javascript
复制
struct S1 {
char a;//(1,8)->1 //char的大小为1,vs默认对齐数为8,选择较小的1为对齐数
int b;//(4,8)->4//int的大小为4,vs默认对齐数为8,选择较小的4为对齐数
int c;//(4,8)->4//同上
}; 

int main()
{

printf("%zd",sizeof(struct S1));

return 0;
}

你认为这串代码的结果是什么,是不是和我刚开始一样,认为结果是6,但其实结果是12,下面我们来说一下为什么?

如下图所示:

其实大致过程就是:第一个元素先放在0处,然后第二个是int类型的,占四个字节,然后要找的对齐数就必须是4的倍数,所以要找到四,然后往下走四个,到达8这个位置,然后有一个char类型的,然后找到9,但是我们还有一点就是结构体的总大小必须为最大对齐数的整数倍,最大对齐数位4,但是9不符合,因此我们需要找到12,所以就会输出12了。

1.3修改默认对齐数:

代码语言:javascript
复制
#include<stdio.h>
#pragma  pack(1)//改变最大对齐数,此时的最大对齐数为1,所以结果就是6了
struct stu
{
	char c1;
	int n;
	char c2;
};
#pragma pack()
int main()
{
	printf("%zd", sizeof(struct stu));
	
	return 0;
}

这个就是将最大对齐数改为11,然后结果就和刚才我们预测的一样了,是6,因为后面所有的数都是1的倍数,所以不需要1跳过什么,直接计算就可以。

那为什么我们要对齐呢,这样不会浪费空间吗,下面我们来看一张图片吧:

看完这张图片是不是恍然大悟了,因为一个字节是四个比特位嘛,所以如果按上面的方式,就会需要访问两次,所以我们对齐,这样会节省很多时间,但是同时会浪费一些空间。

二、结构体传参

结构体传参是怎样进行的呢,下面我们先来看一下这段代码:

代码语言:javascript
复制
//struct stu {
//	int arr[100];
//	int n;
//	double b;
//};
//
//void print1(struct stu tmp)
//{
//	int i = 0;
//	for (i = 0; i < 5; i++)
//	{
//		printf("%d ", tmp.arr[i]);
//	}
//	printf("\n%d\n", tmp.n);
//	printf("%.2lf", tmp.b);

这段代码其实也可以实现结构体的传参,但是我们可以发现,传参时会复制一份原来的结构体,供给函数使用,但是这样会占用更多的内存,下面我们来试一下一级指针传参:

代码语言:javascript
复制
void print2(struct stu* ps)
{
	for (int i = 0; i < 5; i++)
	{
		printf("%d ", ps->arr[i]);
	}
	printf("\n%d\n", ps->n);
	printf("%.2lf", ps->b);
}

int main()
{
	struct stu s = { {1,2,3,4,5},10,3.14 };
	//print1(s);
	print2(&s);
	//这样传递效率会更高一点。
	return 0;
}

看一下这串代码,这串代码便利用了一级指针进行传参,节省了不少空间。

三、结构体位段:

3.1格式:

(1)位段的成员必须是int、unsigned int 或 signed int; (2)位段的成员名后边有一个冒号和一个数字。

下面看一下这串代码:

代码语言:javascript
复制
struct S {
	int _a : 2;//_a只占2bit
	int _b : 5;//-b只占5bit
	int _c : 10;//_c只占10bit
	int _d : 30;//_d只占30bit
}s;//8B

这便是一个位段,我们提前就规定了他们各占几个字节,那样便可以节省许多内存。

同样在内存分配时,也会受这方面的影响。

代码语言:javascript
复制
struct SS {
	char a : 3;//a只占3bit
	char b : 4;
	char c : 5;
	char d : 4;
}ss;
int main() {
	printf("%d\n", sizeof(struct SS));//3;
	ss.a = 10;//1010->010-> 010
	ss.b = 12;//1100->1100-> 0110 0110
	ss.c = 3;//11->00011-> 0000 0011
	ss.d = 4;//100->0100-> 0000 0100
	//&ss -> 0x 62 03 04
	
	return 0;
}

从这串代码的注释即可看出。

内存分配演示图:

总结:

这篇文章我们讲了关于结构体的一些内容,比较重要的就是结构体的内存对齐部分,这方面一定要搞懂才可以。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2024-03-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、结构体
    • 1.1结构体内存对齐:
      • 1.2结构体内存对齐的计算:
        • 1.3修改默认对齐数:
        • 二、结构体传参
        • 三、结构体位段:
          • 3.1格式:
          • 总结:
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档