初识结构体
- 一.结构体声明
- 1.结构体的概念
- 2.声明
- 二.结构体的基础使用
- 三.结构体变量的定义和初始化
- 四.空结构体
- 五.柔性数组
- 1.定义
- 2.使用
- 六.结构体内存对齐
- 七.位端
一.结构体声明
1.结构体的概念
结构体是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量
这里与数组做出区分:数组是一组相同类型元素的集合
结构体主要是用来描述复杂对象,比如一本书,我们需要描述它的内容,作者名,售价…很明显只用int char…类型是不行的
2.声明
至于variable-list是什么请看下文结构体的基础使用
例子:我需要描述一个学生
二.结构体的基础使用
注意结构体的声明只是定义了该结构体的类型(这类型就像是int ,char…是结构体被定义的类型),而s1,s2,s3才是向计算机申请了一块空间
而创建的变量才开辟空间
这里看看variable-list是什么
s1,s2,s3和s4,s5的区别就像是定义一个全局变量i和一个局部变量i的区别
结构体的成员可以是变量,字符,数组甚至是其他结构体
三.结构体变量的定义和初始化
定义其实很简单,一般有三种方法,其中s4,s5,s6是全局变量,s1,s2,s3是局部变量
接下来初始化结构体变量
这里注意括号内的元素要一一与声明内的相对应
那接下来将它打印出来
这里打印也是需要依次对应的,.操作符就是专门访问结构体
接下来使用指针打印
这里的道理其实是一样的传的是s1的地址那么*s1就是s1,然后再用.操作符。
其实这样写有些麻烦,所以c语言有->符号专门访问这种传址调用
四.空结构体
结论是结构体有多大和平台有关系,接下来来讨论一下空结构体有多大
这里可以看出在VS里,空结构体是不能被定义的(如果是c++项目是可以编过的),它要求至少有一个成员。那么在Linux环境下呢?
可以看出,在Linux环境里空结构体是直接编过了并且大小是0.那么我此时就有些好奇了,空结构体能不能定义变量呢?该变量的大小是多少呢?
我们发现空结构体可以定义变量并且该变量的大小是0。那既然空间是0,可不可以存储数据呢?答案是不行的
换言之,在c语言中可以定义一个大小为0的变量,但是因为该变量没有空间,所以不能赋值并且不同的编译器对空结构体的要求不一样,要从多种环境下看
五.柔性数组
1.定义
柔性数组不同于变长数组,它只能被定义在结构体中
对于c语言是不能定义一个大小为0的数组的
但对柔性数组来说,我们一般将它的大小填为0并且编译器是能编过的
其实以上就是一个柔性数组的定义,但根据我们习惯,我们尽量把柔性数组放在最后定义,也就是如下
2.使用
首先要明确的是柔性数组不占空间
柔性数组就是帮助我们开辟空间的
其实严格意思上讲,柔性数组就是将结构体变长
六.结构体内存对齐
看一个例子
两个结构体我们只是调整了顺序但是它们的内存大小却截然不同,这就是因为内存对齐。
1.结构体的第一个成员,对齐到结构体在内存中存放位置的0偏移处。
c1存到偏移量0位置。
2.从第二个成员开始,每个成员都要对齐到对齐数的整数倍数。
对齐数是结构体成员自身大小和默认对齐数的较小数。在VS里,默认对齐数是8;在Linux里没有默认对齐数。
接下来存放i,i自身大小是4而VS的默认对齐数是8,4<8,按照4来对齐。偏移量必须是对齐数的倍数,也就是说i必须从偏移量为4的位置开始储存。
接下来c2的分析方法与i一样。
3.结构体的总大小必须是所有成员对齐数中最大的对齐数的整数倍。
这里最大对齐数是4,那么它的总大小应该是4的倍数,而上面一个9个字节很明显不是4的倍数,所有得继续向下走。
所以stu1的总大小就是12。同理stu1按照这么分析就是8.
再来一个例子
此时在结构体内包含另一个结构体又有新的规则。
4.如果结构体内嵌套结构体,里面的结构体要对其自己最大成员的整数倍。
s3的大小是16(这里不再计算了),而s3里最大对齐数是8,所以s3应该对齐8的整数倍。
接下来就是放d。
从0到31一共32个字节,判断是否为最大对齐数的整数倍(要包含嵌套结构体里的最大对齐数,8)。可以发现最大对齐数是8,而32是8的倍数,所以32就是s4的大小。
为什么要进行结构对齐呢?
下面是内存不对齐情况,a必须读取两次,但在上面内存对齐下,a只需要读取一次就可以了。注意这里内存一次读取多少字节却决于机器。
自定义默认对齐数
上面说到,在VS里,默认对齐数是8.但有些时候这种默认对齐数可能并不合适,所以c语言又提供了一种宏#pragma pack来自定义对齐数。
#pragma pack(1),就是将默认对齐数改为1。而下面的#pragma pack()就是取消修改。意思是4这个修改的对齐数到#pragma pack就结束了。
七.位端
位段的声明和结构是类似的,有两个不同:
1.位段的成员必须是 int、unsigned int 或signed int(char也可以,因为char本质上也是整形) 。
2.位段的成员名后边有一个冒号和一个数字。
位端中的位其实就是比特位。一个整形应该是4个字节,32个比特位。我们看_a后面的2就代表它需要2给比特位,同理_b需要5个比特位,_c需要10个比特位,_d需要30个比特位。总共需要47个比特位,很明显只有两个整形才能装下,故该结构体的大小是8个字节。(位段本身设计就是为了节省空间,所以不用对齐)
内存分配方式
1. 位段的成员可以是 int unsigned int signed int 或者是 char (属于整形家族)类型
2. 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的。
3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。
4. 以上面的例子为例,a+b+c为17个比特位,那么一个整形还剩下15个比特位。下面的d需要30个比特位很明显装不下,需要在开辟一个整形空间,但它究竟是会将剩下的15个比特位占满还是直接从新的空间里开辟,c语言没有明确规定,取决于编译器。
位段的跨平台性
1. int 位段被当成有符号数还是无符号数是不确定的。
2. 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机 器会出问题。
3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
4. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是 舍弃剩余的位还是利用,这是不确定的。文章来源:https://uudwc.com/A/b1yXP
总结:
跟结构相比,位段可以达到同样的效果,但是可以很好的节省空间,但是有跨平台的问题存在。
文章来源地址https://uudwc.com/A/b1yXP