一、结构体
对于 人、书 这样的 复杂对象、基本数据类型是无法表达的,只能表达其中的某一个方面,但将复杂对象的这些方面结合起来,相当于表达了复杂对象,使用结构体便可以达到此目的
1. 结构体声明
struct tag
{
//Member_List
}//Variable_List;
struct 结构体关键字
tag 结构体标签名:符合标识符命名规则即可
Member_List 成员列表:结构体成员的定义
Variable_List 变量列表:结构体类型声明时创建的变量,可以省略
注意:花括号后的分号不能省略
在上述的结构体声明中标签 tag 是可以省略的,称为匿名结构体
需要注意:
- 匿名结构体只能在声明结构体同时创建结构体变量
由于没有标签名无法在声明结构体后创建变量 - 对于两个成员相同的匿名结构体,编译器认为这是两个不同的结构体
2. 结构体变量的创建及初始化
结构体创建变量有如下两种方式:
//在结构体声明的同时创建结构体变量 p1,p2
struct people
{
char name[20];
int age;
} p1,p2 = {"张三", 20};
//在结构体声明后创建结构体变量 p3 (匿名结构体无法创建变量)
int main()
{
struct people p3 = {.age = 18, .name = "李四"};
return 0;
}
由于结构体变量有多个成员,初始化时需要使用花括号
- 结构体变量 p2 在创建的同时按顺序的方式初始化
- 结构体变量 p3 在创建的同时以指定成员的方式初始化
3. 访问结构体成员
结构体变量访问其成员时,通过结构体.成员名的方式
p3.name = "王五";
p3.age = 30;
结构体指针访问其指向的结构体变量的成员时,通过结构体->成员名,或者(*结构体指针).成员名的两种方式
struct people* ps = &p3;
ps->name = "王五";
(*ps).age = 30;
4. 结构体内存对齐
结构体大小并不是结构体所有成员的大小相加后的结果,而是需要遵循四条对齐规则
-
第一个成员存储于 相对结构体变量起始地址 0偏移 位置处
-
第二个及之后的成员存储于 相对结构体变量起始地址 的某个 对齐数 的 整数倍的偏移 位置处
对齐数:成员变量存储所占空间的大小 和 编译器默认对齐数 中的 较小值 -
结构体的总大小需要对齐到 相对结构体变量起始地址 偏移量为最大对齐数的整数倍地址处
最大对齐数:每一个成员的对齐数中的最大值 -
对于 嵌套的结构体变量 对齐到 该嵌套的结构体的最大对齐数 的整数倍地址处
#include <stdio.h>
struct S1
{
char c;
short s;
int i;
};
struct S2
{
char c;
int i;
short s;
};
int main()
{
struct S1 s1;
struct S2 s2;
printf("%d %d", sizeof(s1), sizeof(s2));
return 0;
}
输出
8 12
注意:数组的对齐数以数组元素的对齐数来确认
5. 修改默认对齐数
默认对齐数通过如下指令可以修改
//修改默认对齐数为 num(整形常量)
pragam pack(num)
//...
//恢复编译器默认对齐数
pragam pack()
6. 结构体传参
函数实参和形参之间是以值传递的方式进行的
函数调用时,如果实参传递为结构体变量时,形参也需要创建一个和实参相同大小的结构体来接收,当结构体很大时,时间和空间上都会存在不小的开销
一个更优的方式为传递结构体地址,在函数中通过->操作符可以访问实参的结构体
如果在函数中不希望改变实参所指向的结构体,可以在函数的参数部分加上 const 声明形参
二、枚举
枚举声明如下:
enum tag {Possible_Values_1, Possible_Values_2, ……, Possible_Values_n };
enum 枚举的关键字
tag 枚举标签名:符合标识符命名规则即可
Possible_Values1 枚举可能取值:该值默认为 (int)0,声明时可以赋值
...
Possible_Values1 枚举可能取值:该值默认为上一个枚举可能取值 + 1,声明时可以赋值
注意:花括号后的分号不能省略
在生活中,诸如:颜料三原色,星期一,星期二,……,星期天等,可以采用枚举的方式表达
enum color { RED = 3, YELLOW = 5, BLUE = 10 };
enum week { Monday = 1, Tuesday, Wednesday, Thursday,,Friday, Saturday, Sunday };
三、联合体
在某些场景中,对于一个复杂对象,在某时刻,只会使用他的一个属性时,可以使用联合体
union tag
{
//Member_List
}//Variable_List;
union 联合体关键字
tag 联合体标签名:符合标识符命名规则即可
Member_List 成员列表:联合体成员的定义
Variable_List 变量列表:联合体类型声明时创建的变量,可以省略
注意:花括号后的分号不能省略
联合体的特点:联合体中的多个成员,占用同一片空间,遵循如下规则
- 每一个成员变量都存储于 相对联合体变量的起始地址 偏移量为 0 的地址处
- 联合体的总大小需要对齐到 相对结构体变量起始地址 偏移量为最大对齐数的整数倍地址处
对齐数:成员变量存储所占空间的大小 和 编译器默认对齐数 中的 较小值
最大对齐数:每一个成员的对齐数中的最大值
注意:数组的对齐数以数组元素的对齐数来确认
#include <stdio.h>
union U1
{
char c;
int i;
}u1;
union U2
{
char c[5];
int i;
}u2;
int main()
{
printf("%d %d", sizeof(u1), sizeof(u2));
return 0;
}
输出
4 8
由联合体的特点可知:同一时刻访问联合体同一个变量才有意义
联合体变量的创建、初始化、及访问和结构体是相似的