一.结构体
1.结构体的基础知识:结构体是一些值的集合,这些值称为结构体 的成员变量。成员变量的类型可以是不同的。
相比,数组是一组相同类型的元素的集合。
2.结构体的声明:
a.形式:
b.事例:
3.结构体的特殊声明
a.形式:
b.特例:
如图,上面两个匿名结构体具有相同的内容,一个定义变量s,一个定义匿名结构体指针ps,可以通过ps指向s所在空间,打印出关于s的成员变量。
这是及其特殊的事例,在平时写代码的时候建议不要这样写。因为编译器会认为这两种类型并不相同。即使是本次可以打印出结果,也有相应的警告,从*到*并不兼容。
4.结构体的自引用
如上图所示,是错误示范。因为在结构体在结构体内部嵌套自身定义变量,永远无法求得一个具体的该结构体类型所占空间大小,所以定义错误。
正确写法:
5.结构体类型的重命名
6.结构体的内存对齐规则 重要指星:五颗星
@1.规则:
a.结构体的第一个成员变量与结构体变量偏移量为0
b.其他成员变量偏移量为其本身字节与编译器默认对齐数的较小值的整数倍
vs编译器默认对齐数:8
c.结构体占据内存空间大小的字节为结构体内成员变量相应对齐数中的最大对齐数的整数倍
d. 有嵌套结构体的情况:被嵌套的结构体偏移量为其内成员变量相应对齐数中的最大对齐数的整数倍;而整个结构体的总字节数为成员变量相应的对齐数,被嵌套的结构体对应的对齐数是其其内成员变量相应对齐数中的最大对齐数中的最大对齐数的整数倍。
@2.事例解析:
示例一:
示例二:
因为示例二与示例一是相似的,这里不做过多的相关赘述。只显示示例二中结构体成员变量在内存中的存储关系如何。
注意分辨示例一与示例二:
示例一与示例二的结构体内的成员变量类型相同,而示例一结构体所占内存空间为12个字节,示例二结构体所占内存空间为8个字节。这是为什么?
示例二中占据内存空间小的成员变量集中,浪费掉的空间内存小。
所以,这给我们一个启发:在结构体内部成员变量并不要求顺序时,尽量将占据内存空间小的成员变量集中在一起,节省空间。
示例三:
因为示例三与示例一是相似的,这里不做过多的相关赘述。只显示示例三中结构体成员变量在内存中的存储关系如何。
示例四:
7.编译器默认对齐数的改变
在vs编译器下,默认的对齐数为8.
修改其默认对齐数为2.
修改默认对齐数为1.
默认对齐数的重要性:一般修改默认对齐数通常为2的0、1、2、3等指数。这是因为常见的内置类型char、short、int、double等的字节数为1、2、4、8,这可以保证在内存对齐的情况下,机器可以一次性读取完整的内置类型数据。
比如,结构体内的成员变量均为int类型的,在32为机器下一次性可以读取4个字节的数据,这样读完全结构体的值可以一次性结束。
默认对齐数为1,代表不进行内存对齐,这时不再浪费空间,占据空间内存小。
9.为什么要进行内存对齐?
a.硬件要求。并不是所有的硬件都可以从任意地址读取任意的数值,而是硬件从特定的地址读取特定的数值。如果违反这一法则,可能会造成抛出硬件异常。(等后续我对抛出硬件异常有所了解时,会具体出一篇博客讲解
b.数据读取的次数。如上文所讲,如果结构体内的成员变量均为int类型的,在32为机器下一次性可以读取4个字节的数据,这样读完全结构体的值可以一次性结束。但如果如下图所示
如果不进行结构体内存对齐,在读取结构体内的值时就会造成时间的大量消耗。
另外:16位机器一次性读取16bit的内容,32位机器一次性读取32bit的内容,64位机器一次性读取64bit的内容。这与cpu相关(后期,我也会写有关cpu的博客内容
10.简单介绍offsetof()函数
@1.头文件:<stddef.h>
@2.功能:求结构体类型中成员变量相较于结构体变量的偏移量
@3.形式:
@4.解析:第一个参数表示结构体类型,第二个参数表示结构体类型中成员变量
@5.事例:
11.结构体的传参:
a.方式:传值调用、传址调用
b.两种方式的优劣性:传值调用不可以修改结构体内容,相对讲安全性能高,参数压栈过程中消耗的时间空间比较大;而传址调用可以修改结构体内容,如果不想改变可以在定义前加const加以修饰,相对讲安全性能不差并且灵活性高,参数压栈过程中消耗的时间空间小。(不久我会讲函数压栈这块知识写一篇博客,立了好多flag。好吧,努力拼吧
综上所述,在结构体传参中,建议使用传址调用。
二.终于来到了位段!!!!(位段通过结构体实现
1.要求:a.结构体内成员变量必须为int、signed int、unsigned int、char等属于整型家族的内置类型
b.结构体内成员变量的编写形式:成员变量类型 成员变量名: ;
2.内存分布:
a.结构体内成员变量必须为int、signed int、unsigned int、char等属于整型家族的内置类型
b.编译器每次会根据结构体内成员变量的类型来决定是以4个字节还是1个字节来开辟空间
比如,下面操作均在vs编译器下完成
3.不跨平台性:
@1.在不同编译器,int类型是signed int还是unsigned int是不可知的。
@2.在不同编译器,当一块空间剩余bit位不足以存储下一个变量的数值,是再开辟空间存储还是紧接着存储若不够就再开辟空间是不可知的。
@3.在16位机器,字长为16bit;而若在32位机器,字长为32bit,若定义某变量占据30bit,将这段代码在16位机器上显示就会出现问题。
@4.在向内存中存储数值是从左向右还是从右向左是不可知的。
当然,不能因为位段是不可跨平台性的,就质疑它的性能。位段在节省空间方面可是个厉害的小能手呢!
4.位段应用广泛,主要应用于网络方面。