c语言:结构体(详解)

初识结构体

一.结构体声明

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://blog.csdn.net/m0_73790767/article/details/128013188

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
青葱年少的头像青葱年少普通用户
上一篇 2024年1月11日
下一篇 2024年1月11日

相关推荐