结构体内存计算详解,及scanf函数返回值和匿名结构体的解析

目录

一、库函数scanf返回值解析(包含在头文件stdio.h中)

        延伸:浅谈一下printf函数返回值问题

二、匿名结构体类型

三、结构体所占用内存问题

        1.偏移量的检测

        2.结构体内存对齐的规则

        3.结构体内存对齐存储时的模拟演示

        4.为什么结构体的存储要内存对齐


一、库函数scanf返回值解析(包含在头文件stdio.h中)

        我们在写代码时经常使用库函数scanf,那是否注意scanf有它的返回值,其返回值的意义又是什么呢?

        scanf函数使用时是为了给变量赋值,当scanf读取数据的时候,读到几个数据就会返回和数据的个数相同的整数,如果未能读到数据或者读取失败则返回EOF(EOF的意思是end of file,被宏定义为-1)。               

例如以下程序:

由以上两个不同的输入例子我们可以更加直观的看到scanf的返回值的规律(^z可以取消scanf函数的对数据的读取)。

scanf函数运用举例:

int main()
{
	int i = 0, j = 0, k = 0;
	while (~scanf("%d %d %d", &i, &j, &k))
	{
		//当scanf什么都没读到是返回-1
		//-1补码为11111111111111111111111111111111
		//~(可以将值按位取反),-1的补码全部取反后是0
		//也就是scanf什么都没读到的时候直接跳出while循环
	}
}

        延伸:浅谈一下printf函数返回值问题

        由上面的例子可以看出第一次printf函数打印时是85,5加上换行,也就是我们赋予的值,第二次打印时printf是拿到了上一个printf返回值进行打印的。仔细数一下上一次printf打印出的是四个字符另加上换行符恰好和本次打印出的值一样都是5。由此看来printf函数的返回值是打印出字符的总数再加换行符之类的格式控制指令的和。 

二、匿名结构体类型

        在C语言自定义类型中我们经常会用到结构体,在结构体中存在一种特殊的类型就是匿名结构体。

        当我们声明一个结构体但是没有将它命名时就成了匿名结构体。

例如:

        这时此结构体便是匿名结构体S1

        >该类型结构体不可在编译时再被创建,只能用S1来使用该类型结构体,相当于一次性的结构体。

三、结构体所占用内存问题

struct D
{
	char i;
	int j;
	char o;
}S1;
struct B
{
	char i;
	char o;
	int j;
}S2;

我们现在来看结构体S1和S2,安理来说它们两个所占用的空间大小是相同的

可实际上并非如此:

        >首先在计算结构体内存时我们要知道每个结构体成员的初始存储位置对于结构体存储的初始位置是有差别的,被称为偏移,偏移的多少被称为偏移量

        1.偏移量的检测

                >对于结构体每个成员的偏移量我们用宏offsetof(被定义在头文件stddef.h中)来计算

例如:

                >可知结构体成员之间不是紧挨着内存一个个存储的 

             那它们是按什么样的规律存储的呢?

        2.结构体内存对齐的规则

                <1>结构体的第一个成员直接对齐到相当于结构体变量起始位置为0的偏移处。

                <2>从第二个成员开始要对齐到对齐数的整数倍的偏移处

                         注:>此处的对齐数是取此时存储成员的自身大小相比于默认对齐数的较小值

                                >每个环境下默认对齐数都不同:例如vs是8,Linux环境下不设默认对齐数(就是此时存储成员的自身大小)

                <3>结构体的总大小是整个结构体中最大的对齐数的整数倍

                <4>如果结构体中嵌套了结构体,此嵌套结构体要对齐到自己的最大对齐数的整数倍位置。

                     注:结构体的最大对齐数就是自身所以成员中对齐数最大的那个。

        3.结构体内存对齐存储时的模拟演示

                

struct D
{
	char i;
	int j;
	char o;
}S1;

如结构体S1:

我们模拟画出结构体的存储结构(以1字节为一个单位):

以结构体储存起始位置在右侧标记出偏移量0~14

当存第一个char类型的成员i时,由于是第一个成员所以并不需要对齐以0偏移处为起始直接存:

再存int类型j成员时,它自身大小为4字节,vs默认对齐数是8,所以取较小的4作为实际的对齐数,在4的整数倍的偏移处开使存储: 

这时我们发现1-3偏移处的内存什么也没存被浪费了3个字节

最后存char类型成员o,它自身大小为1字节,vs默认对齐数是8,所以取较小的1作为实际的对齐数,在1的整数倍的偏移处开使存储(就是在8偏移处直接开始存):

此时成员全部存完时共花费的内存是9个字节,但是结构体的总大小是整个结构体中最大的对齐数的整数倍,此时结构体中最大对齐数是int类型j成员的对齐数4,所以此结构体总内存应为4的倍数,此时一个向下再拓展3个字节让结构体总内存达到12:

           4.为什么结构体的存储要内存对齐

                        <1>平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。 

                        <2>性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

                        总体来说:结构体的内存对齐就是拿空间换时间的做法。

        小tips:将结构体内存较小的变量放在一块比较节约空间哦~


今天的博客就码到这里了,感谢大家的阅览~

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

1e-12

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值