自定义类型:结构体·

这篇我们再来接上次的来讲:

1.结构体的内存对齐:

在讲这个知识点之前我们先来做一个题,猜一下:

#include<stdio.h>
struct S
{
	char c1;
	int i;
	char c2;
};

int main()
{
	struct S s = { 0 };
	printf("%zd\n", sizeof(s));//%zd是用来打印无符号整型的

	return 0;
}

我们可以猜一下这串代码的结果是多少:我们一开始是不会感觉char类型是1个字节,int是4个字节,而char还是一个字节,结果就是6个吧。但是答案却是12. 

这是为什么呢?这就是要给大家讲的,关于结构体内存对齐的知识。 

1.1对齐规则

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

1.结构体的第一个成员 对齐 和结构体变量的起始位置偏移量为0的地址处。

2.其他成员变量要对齐到某个数字(这里的某个数字就是对齐数)的整数倍的地址处。

对齐数 = 编译器默认的一个对齐数与该成员变量大小的较小值。(这点非常重要)

vs中的默认对齐数是8

linux中gcc没有默认对齐数,对齐数就是成员自身大小。

3.结构体总大小为最大对齐数(结构体中每个成员变量都有一个对齐数,所有对其数中最大的)的整数倍。

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

下面呢,我们就对这几点细细讲解:

首先就是第一点的起始位置偏移量,这里我们先给出一张图片,当作内存,一个格子就是一个字节

我们可以看到0所代表的那块空间,第1个字节较起始位置的偏移量就是0.第2个字节较起始位置的偏移量就是1,以此类推,这就是偏移量。那我们来看一下第一个是怎么存进去的。

结构体的第一个成员: 对齐 和结构体变量的起始位置偏移量为0的地址处。第一个是char类型的,就占一个格子,就是0对齐的那个格子。

那第二个怎么存进去呢?来看第二条:

2.其他成员变量要对齐到某个数字(这里的某个数字就是对齐数)的整数倍的地址处。

对齐数 = 编译器默认的一个对齐数与该成员变量大小的较小值

vs中的默认对齐数是8

linux中gcc没有默认对齐数,对齐数就是成员自身大小。

我们下来分析一下对齐数是什么概念:

首先我们是用vs进行编译的他的默认对齐数是8,但是第二个成员是int类型的占4个字节,所以这个默认对齐数是4所以第二个成员要对齐到4的整数倍的地址处。又因为0表示的那块空间已经被第一个数占据,第二个成员占据的内存要从4的整数倍开始占用,就是从4这个数开始进行往后在占用4个。

下面给个图理解一下:

看到了没有就是这样,拿下一个成员要占据的内存,也和这个是一个道理。

我们到这里已经占了 9个字节,那下面再来看一下第三条:

3.结构体总大小为最大对齐数(结构体中每个成员变量都有一个对齐数,所有对其数中最大的)的整数倍。

 总大小就是:所有成员中取最大的成员的对齐数的整数倍,但是这里你要把你本身就占据的九个字节包含进去,这三个成员中的最大对齐数就是4,他的整数倍就是4或者8或者12。4,8肯定不行,因为本来占据的字节就不够所以说就是12。

结果就是12了。

那接下来我们可以在对几个题进行练习:

struct S2
{
 char c1;
 char c2;
 int i;
};
int main()
{
struct S2 s = {0};
printf("%d\n", sizeof( S2));
return 0;
}

 我们可以根据上面的讲解来做一下这个题:答案是8

现在对结构体内存对齐的知识点了解的就差不多了 。

那接下来我们再来说一下关于结构体内存对齐规则的第四点:

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

下面还是一个例题进行讲解:

#include<stdio.h>
struct S3
{
	double d;
	char c;
	int i;
};
struct S4
{
	char c1;
	struct S3 s3;//这个s3就是嵌套在s4里面的结构体,那怎么理解第四个规则呢?就像这样
//s3从哪存放呢?要看s3中的最大对齐数,s3所占的字节大小是16,但是我们是用的vs进行编译的,所以对齐数就是8,但s3是占16个字节的,他要从8的倍数处往后数16个字节才可以。
	double d;
//把这些成员都存完,占用了32个字节,但是这个结构体是不是就是占用32个字节呢?还要看这个规则的最后一句话:结构体的整数大小就是所有最大对齐数(含嵌套结构体中成员的对齐数)的整数倍。
//这个成员的所有对齐数最大的就是8,而32就是8的整数倍。
};
int main()
{
	struct S4 s = { 0 };
	printf("%d\n", sizeof( s));
	return 0;
}

这个题会稍微有一些复杂,我在代码中进行讲解。 

通过这几个题的练习我们可以知道:结构体成员所占用的总内存大小并不是结构体占用的内存大小。对于有没有嵌套我们分为两类

没有嵌套的情况:结构体的大小是最大对齐数(结构体中每个成员变量都有一个对齐数,所有对其数中最大的)的整数倍。(这就是最后一步的判断)。

有嵌套的情况:结构体整数大小就是所有最大对齐数(含嵌套结构体中成员的对齐数)的整数倍。(这也是最后一步的判断)。

这里还有一点非常重要,就是对齐数是看你是从什么地址处开始存放的而存放多少字节还是要看你这个成员的大小。虽然说对齐数是要默认对齐数和成员变量大小进行比较,但是该占多少字节还是多少字节,和对齐数没有关系的。关于这一点你可以参考上面的代码。

内存对齐很浪费空间但是为什么还要内存对齐呢?接下来我来讲解一下:

1.2为什么存在内存对齐

这个问题有几个原因第一个就是:

1.平台原因(移植原因):

不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。

2.性能原因

那怎样在设计结构体的时候,我们既要满足对齐,又要节省空间,如何做到:

那就是让占用内存少的集中在一起:就想这样

struct S1
{
 char c1;
 int i;
 char c2;
};
struct S2
{
 char c1;
 char c2;
 int i;
};

我们可以看一下s1和s2他两个的类型都是一样的,但是s1和s2所占的空间的大小就是有区别s2占用的内存就是小。

我们知道vs的默认对齐数就是8,那我们能不能修改一下呢?下面继续讲解:

1.3修改默认对齐数 

#pragma这个预处理的指令,可以改变编译器的默认对齐数。

#include <stdio.h>
#pragma pack(1)//设置默认对齐数是1,那结果是什么来看一下:
struct S
{
 char c1;
 int i;
 char c2;
};
#pragma ();//取消设置的对齐数,还原为默认对齐数,但是设置的对齐数对上面的结构体是有效的
int main()
{
 //
 printf("%d\n", sizeof(struct S));
 return 0;
}

就像是这样。 当你感觉结构体在对齐方式不合适的时候,我们可以自己更改默认对齐数。

OK了兄弟们今天的讲解完毕了,这个知识点还剩下一些内容我们明天讲完。

  • 39
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值