目录
1.结构体的类型
结构体是C语言中可以自定义的数据类型,它可以包含多个不同类型的成员变量,这些变量会同时储存在内存中,可以使用它来表示一个实体的属性或者一组相关的数据。
定义它的关键字是struct,后面跟着可以自定义的名称和括号,在括号中可以定义成员变量的类型和大小,每一个变量由类型和名称组成,用分号分隔。在结尾括号的分号前可以自定义添加结构体变量。
结构体示例:
在示例中,我们定义了一个名字为student的结构体,它有三个成员变量name,age和gender,它们分别表示名字,年龄,性别。
结构体可以通过使用 . 运算符来进行访问成员变量。
注意:只有在定义结构体student结尾括号的分号前定义的结构体变量是全局变量,其他则是局部变量
这里我定义了一个结构体局部变量S同时自定义成员变量,通过使用 .运算符我成功将年龄打印了出来。
注意:如果进行了结构体变量的地址传参,那么就不再使用 .运算符进行访问成员变量,需要使用->来进行访问成员变量
2.结构体的在内存中的储存
在前面创建的结构体student,它的大小是多少呢?
这里结构体在内存中的存储是连续的一块空间,在结构体储存时会出现内存对齐,但为什么结构体student的大小是12呢?
首先得了解内存对齐的规则:
1.结构体的第一个成员对齐到结构体变量起始位置偏移量为0的地址处
2.其他成员变量要对齐到某个数字(对齐数)的整数倍数的地址处
对齐数:编译器默认的一个对齐数与该变量成员大小的较小值,如果没有默认对齐数,对齐数就是成员自己的大小
3.结构体的总大小是最大对齐数的整数倍。
4.如果嵌套了结构体的话,嵌套的结构体成员对齐到自己的成员中最大对齐数的整数倍处,
结构体的整体大小就是所有最大对齐数(包含嵌套的结构体中的成员的对齐数)的整数倍。
如图:
char name为一个字节对齐到0的位置,但后面的int age是四个字节那么就会浪费3个字节到4的位置,此时4为int四个字节的倍数所以int age从4的位置开始储存,char gender大小也1字节所以直接存入8的位置即可,此时结构体变量S一共使用了9个字节,但9并不是最大对齐数4的倍数,此时会再浪费三个字节到11的位置上,此时一共使用了12个字节为4的倍数,所以结构体变量S总大小为12个字节。
如果想要节省空间,可以优先将字节数小的成员放在前面。
3.为什么会存在内存对齐呢?
1.不同平台的原因(移植原因)
有的硬件平台不能够随意访问地址上的数据,只能访问特定地方的数据,否则会出现错误
2.性能原因
数据结构应该尽量向自然边界上对齐,因为在访问未对齐的内存,处理器需要进行两次内存访问,而对齐的内存访问只需要一次。设一个处理器总是从内存中读取4个字节,则地址必须是4的倍数,如果我们能够保证所有int类型的数据的地址都对齐为4的倍数,那么就可以进行单次读取,不会出现一个对象放在两个4个字节的内存块中需要读取两次的情况。
总体来说:结构体的内存对齐是将空间浪费来换取时间的做法。
4.位段
结构体能够实现位段自定义结构体成员的大小的能力,位段的声明如下:
1.位段的成员必须是int,unsigned int或者signed int类型或者char类型
2.位段的成员需要在结构体成员变量后面加上一个冒号和数字
位段的内存分配注意事项:
1.位段的空间分配是以一个字节(char)或者四个字节(int)的方式进行开辟
2.它进行自定义大小是以bit位来定义大小
位段能够自定义结构体成员的大小如图:
这里将他们自定义为1,2,4个bit位加起来并未超过8个bit位,所以得出大小为1个字节
将2,10,5转换为二进制为0010,1010,0101,此时会发现P1的分配的空间不够出现截断,它只能存下低位0,P2只能存入10,P3倒是没什么事,由此可见在使用位段时应该注意赋予值的大小。
注意:
1.如果自定义的大小已经将第1个字节填至不够剩余成员的大小,则浪费第一个字节内的剩余空间存入第二个字节。
2.由上面的图可以看出位段有着许多的不确定因素,如果注重移植性应该避免使用位段
3.位段在内存中的储存并没有准确的方向,这里我是从右到左,实际也有可能是从左到右
感谢你能够读到这里,希望本篇文章对你有帮助。