自定义类型
1.结构体
C语⾔已经提供了内置类型,如:char、short、int、long、float、double等,但是只有这些内置类型还是不够的,假设我想描述学⽣,描述⼀本书,这时单⼀的内置类型是不⾏的。
描述⼀个学⽣需要名字、年龄、学号、⾝⾼、体重等;
描述⼀本书需要作者、出版社、定价等。C语⾔为了解决这个问题,增加了结构体这种⾃定义的数据类型,让程序员可以⾃⼰创造适合的类型。
结构是⼀些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量,如:标量、数组、指针,甚⾄是其他结构体。
1.1结构的声明
//描述一个学生
struct Stu
{
char name[20];//描述名字
int age;//描述年龄
char sex[5];//描述性别
char id[20];//描述学号
};//这个不能丢
1.2结构体变量的创建和初始化
1.2.1普通声明
1.2.2 嵌套声明
直接在分号后创建变量并初始化
1.2.3 不完全声明(匿名结构体)
适用于只使用一次的结构体
1.3结构成员访问操作符
1.3.1结构体成员的直接访问
1.3.2结构体成员的间接访问
1.4结构体的自引用
我们将一系列数据存在内存中,不管我们以何种方式存储都必须做到能找到第一个数据和下一个数据,因此我们定义一个结构体能找到第一个数据且能找到下一个数据的地址
struct Node
{
int data;
struct Node* next;
};
在结构体⾃引⽤使⽤的过程中,往往夹杂了 typedef 对匿名结构体类型重命名
1.5结构体内存对齐
计算结构体的内存大小
1.5.1对齐规则
注意:当成员变量为数组时,它的对齐数取决于数组中存放的变量类型也就是说char[5]的对齐数为1而不是5
练习(vs平台下)
偏移量为0的位置是任何对齐数的0倍
结构体struct S4的大小大家可以自己画表格尝试一下,同时提供给大家一个求偏移量的函数
offsetof(结构体,成员);
1.5.2为什么存在内存对齐
那我们如何节约内存空间呢,只要让占用空间小的变量集中在一起就行了(而顺序是是不做要求的也就是说两个char类型的变量和两个int类型的变量,只要char紧挨着就行另外的就不用考虑了)
struct S1
{
char a;
char b;
int c;
int d;
};
struct S2
{
int c;
char a;
char b;
int d;
};
struct S3
{
int c;
int d;
char a;
char b;
};
这四者大小相同
1.5.3 修改默认对齐数
由实现定义,可能要跟硬件相匹配。
1.6结构体传参
我们往往倾向于print2
1.7结构体实现位段
1.7.1什么是位段
那我们接下来得先了解位段的内存分配
1.7.2位段的内存分配
在VS平台上
- 位段的空间上按照需要以4个字节(int)或者一个字节(char)来开辟的,空间是由右向左去利用的
- 当剩下的空间存放不下下一个成员时,剩下的空间会被浪费
例子:
未上色的是未利用或舍弃的
结果和分析的答案一样
大家可以自己算一下这个在内存中的存储
注意:一个int可以放32个比特位,_a占用2个,_b占用5个,_c占用10个,共使用17个,然后_d放不进去,所以要放在第二个基本类型中。也就是说int要去浪费空间是从四个字节来看,注意这一点
可以尝试一下
1.7.3位段的跨平台问题
大家可以看到上面是如何分析的,不同编译器可能不同。因为标准C并没有定义,但是大家可以通过调试去验证
1.7.4位段的应用
1.7.5 位段使用的注意事项
位段的⼏个成员共有同⼀个字节,这样有些成员的起始位置并不是某个字节的起始位置,那么这些位置处是没有地址的。内存中每个字节分配⼀个地址,⼀个字节内部的bit位是没有地址的。所以不能对位段的成员使⽤&操作符,这样就不能使⽤scanf直接给位段的成员输⼊值,只能是先输⼊放在⼀个变量中,然后赋值给位段的成员。
2.联合体
2.1 联合体类型的声明
像结构体⼀样,联合体也是由⼀个或者多个成员构成,这些成员可以不同的类型。
但是编译器只为最⼤的成员分配⾜够的内存空间。联合体的特点是所有成员共⽤同⼀块内存空间。所以联合体也叫:共⽤体。
给联合体其中⼀个成员赋值,其他成员的值也跟着变化。
2.2联合体的特点
联合的成员是共⽤同⼀块内存空间的,这样⼀个联合变量的⼤⼩,⾄少是最⼤成员的⼤⼩(因为联合⾄少得有能⼒保存最⼤的那个成员)。
2.3联合体大小的计算
正如我前面强调数组的对齐数取决于数组的元素,Un1的大小为最大成员的大小也就是5,但5不是最大对齐数int 4的倍数,浪费三个空间,联合体的大小为八个字节。Un2大家自己试试
使⽤联合体是可以节省空间的,举例:
⽐如,我们要搞⼀个活动,要上线⼀个礼品兑换单,礼品兑换单中有三种商品:图书、杯⼦、衬衫。
每⼀种商品都有:库存量、价格、商品类型和商品类型相关的其他信息。
图书:书名、作者、⻚数
杯⼦:设计
衬衫:设计、可选颜⾊、可选尺⼨
那我们不耐⼼思考,直接写出⼀下结构:
我们注意到:三件商品都有库存量、价格、商品类型(要重复使用),书名、作者、⻚数(独有只使用一次),同理
故所以我们就可以把公共属性单独写出来,剩余属于各种商品本⾝的属性使⽤联合体起来,这样就可以介绍所需的内存空间,⼀定程度上节省了内存。
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;
};
这里union和struct都采用了匿名声明,因为独有的我们仅仅使用一次
3.枚举类型
3.1枚举类型的声明
枚举顾名思义就是⼀⼀列举。
把可能的取值⼀⼀列举。
⽐如我们现实⽣活中:
⼀周的星期⼀到星期⽇是有限的7天,可以⼀⼀列举
性别有:男、⼥、保密,也可以⼀⼀列举
⽉份有12个⽉,也可以⼀⼀列举
三原⾊,也是可以意义列举
这些数据的表⽰就可以使⽤枚举了
3.2枚举类型的特点
3.3 枚举类型的使⽤
#include<stdio.h>
typedef enum
{
FALSE,
TRUE
}Bool;
int main()
{
Bool a = TRUE;
if (a)
printf("真");
return 0;
}
枚举是用来稀疏地掩饰整数的因为往往我们不会把数字与字符串对应起来,枚举解决来这一点。
虽然把枚举的值(字符串)作为整数使用非常方便,但是把整数用作枚举的值却是非常危险的。我们往往通过枚举枚举常量给枚举赋值
也就是Bool a=TRUE;尽管C语言上并没有在这要求,但是C++上不允许整数作为枚举的值来使用,因此我们这里最好统一。