大家好久不见,今天我们来学习一下C语言中的自定义类型:
C语言的自定义类型包括:结构体,枚举和联合,接下来大家跟我来一起认识一下这三种类型。
目录
1. 结构体
1.1.1 结构体类型的声明
1.1.2 结构的特殊声明
1.1.3 结构体的自引用
1.2. 结构体的内存对齐
1.2.1 对⻬规则
1.2.2 为什么有内存对齐
1.2.3 修改对齐数
1.3 结构体实现位段
1.3.1 什么是位段
1.3.2 位段的内存分配
1.3.3 位段的问题
2. 联合体
2.1.1 联合体类型的声明
2.2 联合体的特点
2.2.1 联合体大小的计算
2.3 联合体的使用案例
3. 枚举类型
3.1 枚举类型的声明
2.2 枚举类型的优点
1. 结构体
1.1.1 结构体类型的声明
struct tag{member- list ;}variable- list ;
例如我们要写一个学生的结构体,包含学生的年龄,性别,姓名和学号,
struct Stu{char name[ 20 ]; // 名字int age; // 年龄char sex[ 5 ]; // 性别char id[ 20 ]; // 学号}; // 分号不能丢
1.1.2 结构的特殊声明
在声明结构的时候,可以不完全的声明。
struct{int a;char b;float c;}x;struct{int a;char b;float c;}a[ 20 ], *p;
当创建无命名的结构体时,我们称该结构体为匿名结构体,那这种结构体有没有什么问题呢?
p=*x;
当执行p=*x代码时,编译器会将这个结构体认成是两种不同的类型,但实际上这是同一个结构体。
所以对于匿名结构体来说,应该将所有该结构体变量在声明时创建。
1.1.3 结构体的自引用
struct Node{int data;struct Node next ;};
那 么将结构体作为它自身的一个成员 这样行不行得通呢?
如果这样的话,在计算结构体类型的大小时就会像套娃一样无穷的计算下去。所以是不可行的。
那么如果我们想让结构体作为成员,应该怎么来声明呢?
我们知道,指针的大小只与机器本身有关,如果我们使用指向结构体的指针作为成员,不就解决这个问题了吗?
struct Node{int data;struct Node* next ;};
那如果我们在定义结构体时对结构体进行了typedef重命名,在指针部分可以使用重命名的名字吗?
答案是不行的,因为结构是在成员创建之后才进行的重命名,所以要用原名。
typedef struct{int data;Node* next; //错误}Node;typedef struct{int data;struct Node* next; //正确}Node;
1.2. 结构体的内存对齐
1.2.1 对⻬规则
1. 结构体的第⼀个成员对⻬到和结构体变量起始位置偏移量为0的地址处。2. 其他成员变量要对⻬到某个数字(对⻬数)的整数倍的地址处。对⻬数 = 编译器默认的⼀个对⻬数 与 该成员变量⼤⼩的较⼩值。- VS 中默认的值为 8- Linux中 gcc 没有默认对⻬数,对⻬数就是成员⾃⾝的⼤⼩3. 结构体总⼤⼩为最⼤对⻬数(结构体中每个成员变量都有⼀个对⻬数,所有对⻬数中最⼤的)的整数倍。4. 如果嵌套了结构体的情况,嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处,结构体的整体⼤⼩就是所有最⼤对⻬数(含嵌套结构体中成员的对⻬数)的整数倍。
struct S1{char c1;int i;char c2;};
因为结构体的第⼀个成员对⻬到和结构体变量起始位置偏移量为0的地址处。所以结构体中第一个char类型占内存大小为1,位置从0到1,而对于int来说,它的对齐数为4,位置应该从4到8,对于第二个char类型来说,它的对其数为1,所以可以从位置为8的位置紧接着,位置为8到9。所有的成员类型都花完了,在计算结构体大小时,还要考虑最大对其数,在该结构体中为4,所以结构体的大小为4的整数倍,对与该结构体来说,大小应该为12。
1.2.2 为什么有内存对齐
struct S1{char c1;char c2;int i;};
此时结构体的大小为8,节省了空间。
1.2.3 修改对齐数
# include <stdio.h># pragma pack(1) // 设置默认对⻬数为 1struct S{char c1;int i;char c2;};# pragma pack() // 取消设置的对⻬数,还原为默认int main (){printf ( "%d\n" , sizeof ( struct S));return 0 ;}
1.3 结构体实现位段
1.3.1 什么是位段
struct A{int _a: 2 ;int _b: 5 ;int _c: 10 ;int _d: 30 ;};
位段与结构体十分相似,那么位段的大小又是多少呢?下面我们来学习一下位段的内存分配。
1.3.2 位段的内存分配
struct S{char a: 3 ;char b: 4 ;char c: 5 ;char d: 4 ;};struct S s = { 0 };s.a = 10 ;s.b = 12 ;s.c = 3 ;s.d = 4 ;
在位段声明时冒号后带的数字为该成员的空间大小,而数据是在创建变量后才能赋值。
这就是S位段的内存大小,当一个字节内剩余空间大于下一个成员的大小时,下一个成员将会存放到这个字节中。
1.3.3 位段的问题
2. 联合体
2.1.1 联合体类型的声明
union Un{char c;int i;};int main (){// 联合变量的定义union Un un = { 0 };// 计算连个变量的⼤⼩printf ( "%d\n" , sizeof (un));return 0 ;}
结算的结果是4,那为什么是这个结果?我们继续往下看。
2.2 联合体的特点
struct S{
char c;int i;};union A{char c;int i;};
在联合体中,成员公用一块空间,所以改变一个成员的值时,其他成员的值也会跟着改变。
2.2.1 联合体大小的计算
根据上文,我们可以知道:联合体的大小至少是最大成员的大小,而当最⼤成员⼤⼩不是最⼤对⻬数的整数倍的时候,就要对⻬到最⼤对⻬数的整数倍。
2.3 联合体的使用案例
联合体的使用可以节省空间,
struct gift_list //不使用联合体
{
//公共属性
int stock_number;//库存量
double price; //定价
int item_type;//商品类型//特殊属性
char title[20];//书名
char author[20];//作者
int num_pages;//⻚数char design[30];//设计
int colors;//颜⾊
int sizes;//尺⼨
};
struct gift_list //使用联合体
{
int stock_number;//库存量
double price; //定价
int item_type;//商品类型union {
struct
{
char title[20];//书名
char author[20];//作者
int num_pages;//⻚数
}book;
struct
{
char design[30];//设计
}mug;
struct
{
char design[30];//设计
int colors;//颜⾊
int sizes;//尺⼨
}shirt;
}item;
};
3. 枚举类型
3.1 枚举类型的声明
enum Day//星期
{
Mon,
Tues,
Wed,
Thur,
Fri,
Sat,
Sun
};enum Sex//性别
{
MALE,
FEMALE,
SECRET
};
enum Color//颜⾊
{
RED,
GREEN,
BLUE
};
enum Color//颜⾊
{
RED = 2,
GREEN = 4,
BLUE = 8
};
2.2 枚举类型的优点
那么我们今天的学习就到这里啦,我们下次再见。