1. 结构体回顾
结构体的声明
结构体的初始化
2. 结构体的特殊声明
匿名结构体: 不需要给结构体名字,但是只能使用一次。
这里的使用一次具体是什么意思呢,刚开始学的时候我自己的理解是有误解的,下面给出一个示例;
注意:这里的匿名结构体只能使用一次的意思是它只能初始化一次,也就是说只能跟在匿名结构体的后面进行初始化,这就和正常的结构体不同,因为正常的结构体是可以在main函数里面或者结构体外面初始化的,但是并不代表着只能初始化一个,也可以初始化多个。
我们通过上面的示例可以发现匿名结构体不能像正常的结构体那样完成初始化,那没有其他方法对其进行初始化了吗?其实我们通过一些手段是可以对其进行初始化的
看下面这个示例:
这里我们用到了strcpy函数来将要初始化的内容拷贝到了目的地,也达到了我们想要的目的。
3. 结构体的自引用
在讲结构体的自引用之前我们先回顾一下数组,数组其实就相当于一个顺序表,因为我们如果知道一个元素的地址的话,是可以顺藤摸瓜去找到其他的元素的,
如下图图示:
我们这里在每一个结构体中存放一个数据,然后在将下一个结构体数据的地址给到第一个结构体,让第一个结构体可以找到下一个结构体,这样就将这些结构体链接起来了。
那么问题来了,我们该怎么编写代码呢?
刚开始我们可能会这样写:
乍一看是没啥问题,但你仔细一想,这样写合适吗,你知道它会占用多大内存吗?
所以这样写就是不太合适的
修改:
这里这么改动的话就是指针只会占4/8个字节,这样的话就明确了,不像前面的一个结构体是一个int的字节再加上下一个结构体的空间,你不知道是多大的空间。
实现形式:
优化:
这里的结构体自引用最好是好好理解一下,这跟后面要学习的数据结构密切相关,最好是能加深理解。
4. 结构体的内存对齐
(1) 例题1
输出结果:
可以看到结果和我们想的并不一样,那么这是为什么呢?我们下面通过对结构体中内存的存储来进行学习
例题1解读;
S1:
S2:
对比: 我们可以发现即使是相同的成员,占用的内存空间也不相同,为什么呢,
这就和成员的顺序有关了,我们不难发现如果将占内存小的数据集中放在一块的话就可以省下不少的空间。
下面我们来看一个比较特殊,也是一个比较出错的地方—嵌套结构体
(2) 例题2
解题:
所以S3就是16个字节
下面来分析S4:
运行结果:
(3)为什么存在内存对齐呢?
- 平台原因(移植原因):
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。 - 性能原因:
数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要⼀次访问。假设⼀个处理器总是从内存中取8个字节,则地址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对齐成8的倍数,那么就可以用⼀个内存操作来读或者写值了。否则,我们可能需要执行两次内存访问,因为对象可能被分放在两个8字节内存块中。
总体来说:结构体的内存对齐是拿空间来换取时间的做法。
如下面图示:
当对齐时虽然浪费了空间,但只需要读取一次,不对齐时虽然节省了空间,但需要读取两次,一次对齐,一次读取
(4) 计算成员在结构体中的偏移量
可以发现这里计算出来的偏移量和我们分析的一样,说明我们的分析是正确的。
(5) 修改默认对齐数
我们可以通过#pragma
这个头文件来修改默认对齐数
注意: 虽然说设置默认对齐数的时候是任意的,但是考虑到各种类型数据的字节一般都是2的倍数,所以在设置字节的时候最好是偶数。
(6) 结构体传参
方式1:
方式2:
对比: 上面的两种方式中,方式1是传值操作,会将所有的数据都传过去,也就导致了在Print1
函数中,它还会开辟一个同等大的空间,这样其实就比较浪费空间,而方式2是传址操作,传过去的是结构体的指针,而我们知道指针只会占用4/8个字节,这样的话就比较节省空间,当然这里可能会有同学说指针不安全,我们其实完全可以控制的,在传址时加上一个const
就可以了。
5. 结构体实现位段
(1)什么是位段?
位段的声明和结构是类似的,有两个不同:
- 位段的成员必须是
int
、unsigned int
或signed int
,在C99中位段成员的类型也可以
选择其他类型。 - 位段的成员名后边有⼀个冒号和⼀个数字。
示例:
但要注意的是这里冒号后面的数字的大小指的是比特位。
(2)位段的作用
结果:
这里我们可以体会到位段的作用,它可以通过指定空间,从而更好的节省空间
(3)位段中的内存分配
例题:
运行结果:
位段的跨平台问题
位段使用时的注意事项
示例:
正解:
这个时候可以先定义一个变量,然后输入一个值,再把这个值赋给位段。
总结:
这期里面我们主要讲解了结构体的特殊声明、结构体的自引用、结构体的内存对齐、为什么存在内存对齐、修改默认对齐数、结构体传参、结构体实现位段、什么是位段、位段的作用、位段中的内存分配、位段的跨平台问题、使用位段时的注意事项。