结构体定义与声明
定义:
结构体是一种用户自定义的数据类型,它允许你将不同类型的数据(区别于数组)组合成一个整体。例如,如果你要表示一个学生的信息,可能包括姓名(字符串)、年龄(整数)、成绩(浮点数)等不同类型的数据。
声明:
struct(关键字)+tag(自己设置的结构体标签)
{
member-list;(成员列表)
}variable-list(变量列表);
!!!!students不是变量名
此外除了在变量列表定义结构体变量,也能在结构体声明后定义变量
结构体成员的访问
两种操作符
(一) ->
这是一个双目操作符,左测为结构体变量的指针,右侧为成员名称
(二) .
这也是个双目操作符,左测为结构体变量的名称,右侧为成员名称
结构体变量的初始化
1.结构体变量可按照成员列表顺序初始化
2.结构体变量可按照特定顺序初始化(运用结构体成员访问操作符)
结构体内存对齐
在C语言内置数据类型中,每个类型所占的字节大小是一定的,如一个int占4字节
那么结构体变量呢?
如果是简单的将每个成员所占字节数相加结果是6
然而实际上结果是12
这是因为结构体发生了内存对齐
内存对齐的四大规则与对齐数
对齐数
结构体的对齐数是成员变量自身大小和默认对齐数(一般编译器默认对齐数是 8 字节,有些编译器可能不同)中较小的那个值(没有默认对齐数则对齐数即为变量自身大小)。
上述代码在内存中的实际存放方式
修改默认对齐数
可以用到#pragma pack(n)
例如#pragma pack(1)
设置默认对⻬数为1
#pragma pack()
修改后再次使用可取消设置的对⻬数,还原为默认
规则1.
结构体的第⼀个成员对⻬到和结构体变量起始位置偏移量为0的地址处(简单来说即从结构体变量起始位置开始)
如图c1放在了初始位置
规则2.
其他成员变量要对⻬到对⻬数的整数倍的地址处。
如图i的数据类型为int,大小为4,默认对齐数是8,较小值是4,因此对齐数4,因此i会放在4,8,…,4n处,因4未被占用所以从4开始
c2数据类型为char,大小为1,默认对齐数是8,较小值是1,因此对齐数是1,同理c2可以放在1,2,…,n处,因为8未被占用所以从8开始
规则3.
结构体总⼤⼩为最⼤对⻬数(结构体中每个成员变量都有⼀个对⻬数,所有对⻬数中最⼤的对齐数)的整数倍
按照前两条可以知道此时长度仅是9(0-8长度为9),此时成员的对齐数分别为1,4,1最大对齐数为4,结构体的总大小为最大对齐数的整数倍(4n),由于此时长度为9,为了开辟足够空间,得出的最小n值为3,因此总长度为12。
规则4.
如果嵌套了结构体的情况(结构体成员包含另一个结构体),嵌套的内层结构体的对齐数是该结构体成员的对齐数中最大的一个
注意事项
结构体本身不占据内存空间,定义的结构体变量占据内存空间
结构体是一种自定义的数据类型,它本身类似于一个模板,用于定义数据的组织形式。就像一个建筑蓝图,它规定了建筑物(结构体变量)的结构和布局,但蓝图本身(结构体类型定义)并不占据实际用于存放建筑材料(数据)的空间
为什么存在内存对⻬
结论:结构体的内存对⻬是拿空间来换取时间的做法
匿名结构体
定义:匿名结构体是一种没有名字的结构体类型
局限性:匿名结构体的主要局限在于它没有类型名,所以不能在定义它的作用域(例如定义于函数内)之外使用,一般仅能使用一次
使用场景:和普通结构体一样,匿名结构体可以将相关的数据封装在一起。例如,在一个函数内部,如果只需要临时组合一些数据来传递或使用,可以使用匿名结构体
结构体传参
两种方式
更优解是传递结构体指针
函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。
如果传递⼀个结构体对象的时候,结构体过⼤,参数压栈的的系统开销⽐⼤,所以会导致性能的下降