1 结构体
在【C语言初阶】中我们学习了结构体相关的基本概念(包括初始化,声明,传参),在这里进一步深入了解。
1.1 特殊的声明
结构体在声明的时候可以不完全声明,例如:匿名结构体
匿名结构体类型只能使用1次
p是结构体类型的指针,x是结构体变量 虽然这两个结构体成员变量一致,但是从编译器的角度认为 这二者还是两个不同的结构体,会报警告
1.2 结构体自引用
在结构体中包含一个类型为该结构本身的成员是否可以?
在结构体当中再包含一个同样的结构体,struct Node 的字节空间也就是内存大小不可知,编译器报错显示 struct Node next 未定义
而且Node中可再包含1个Node ,不断套娃下去
将struct Node* 变成指针的形式(指针的字节大小是确定的),数据结构中的链表采用的就是数据域,指针域
将匿名结构体typedef 重命名为Node ,但是这样是不可行的。
是因为匿名结构体当中就已经存在Node * 类型的指针 但是在typedef Node之前并不存在Node类型的变量,(类似于先有鸡还是先有蛋)
正确的typedef形式
在typedef 之后 struct Node n1 和Node n1 (创建结构体变量)一致
1.5 结构体变量的定义和初始化
struct Point
{
int x;
int y;
}p1; //声明类型的同时定义变量p1
struct Point p2; //定义结构体变量p2
//初始化:定义变量的同时赋初值。
struct Point p3 = {x, y};
struct Stu //类型声明
{
char name[15];//名字
int age; //年龄
};
struct Stu s = {"zhangsan", 20};//初始化
struct Node
{
int data;
struct Point p;
struct Node* next;
}n1 = {10, {4,5}, NULL}; //结构体嵌套初始化
struct Node n2 = {20, {5, 6}, NULL};//结构体嵌套初始化
1.6 结构体内存对齐(重点)
两个结构体成员变量相同,只是顺序不一样,所占空间不同
这就涉及到结构体内存对齐
vs编译器的默认对齐数是8,gcc等其他编译器没有默认对齐数,就是成员变量自身大小
offsetof (宏) 返回的是结构体成员变量在结构体当中的偏移量
说明结构体内存对齐是存在的。
s3和s4的大小分别是16和32
是s1和s2结构体的成员变量相同,所占空间却不同,是因为s2中所占空间小的成员存放在一起
1.7 修改默认对齐数
#pragma 预处理指令 改变默认对齐数
#pragma pack(8)//设置默认对齐数为8
struct S1
{
char c1;
int i;
char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认
#pragma pack(1)//设置默认对齐数为1
struct S2
{
char c1;
int i;
char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认
1.8 结构体传参
在传址调用的时候,为了防止指针所指向的内容被修改可以加个const修饰
结论
结构体传参要选择传结构体地址
2 位段
2.1 什么是位段
位段的声明与结构体类似,有两个不同点:
1.位段的成员必须是整型家族的,例如:int,char
2.位段的成员名后面有1个冒号和数字(冒号后面的数字表示的是该元素的字节数)
不能超过类型的二进制位 例如char 类型不能超过8位 int类型不能超过32位…
位段中通常放置的都是同类型的元素(位段本身不太稳定,不适合太复杂)
2.2 位段的内存分配
位段是1个字节或者4个字节开辟的,不存在大小端字节序的问题。 大小端字节序是发生在多个字节的情况下
(vs编译器下位段的内存分配)字节的使用情况,从右向左使用(先使用低位空间),如果内存空间不够,再重新开辟一个,之前多余的内存空间直接舍弃。元素也不宜过大,若存放不下舍弃高位
2.3 位段的跨平台问题
在vs编译器上可以运行的代码,在16位机器上会出现问题,所以使用位段要针对不同平台编译不同代码
总结
跟结构体相比,位段可以达到同样的效果,一定程度上可以节省空间,但是存在跨平台的问题
2.4 位段的应用
IP数据报 位段在网络中应用广泛,很好的节省数据的大小,方便网络上的传输
3 枚举
3.1 枚举类型的定义
枚举顾名思义就是一一列举,把可能的取值一一列举
enum Day//星期
{
Mon,
Tues,
Wed,
Thur,
Fri,
Sat,
Sun
};
enum Sex//性别
{
MALE,
FEMALE,
SECRET
};
enum Color//颜色
{
RED,
GREEN,
BLUE
};
枚举enum Color… 是枚举类型,类似于int,char
{ }中的内容是枚举类型的可能取值,也叫 枚举常量
这些可能取值都是有值的,默认从0开始,一次递增1,当然在定义的时候也可以赋初值。
enum Color//颜色
{
RED=1,
GREEN=2,
BLUE=4
};
3.2 枚举类型的优点
为什么使用枚举?通过 #define 定义常量,为什么非要使用枚举?下面来介绍一下枚举的优点
优点1. 增加代码的可读性和可维护性
在实现通讯录的过程中switch case 1 ,case 2 跟函数的实现不搭配,完全是自己定义的
可以通过枚举的方式优化
// 枚举类型
enum Option
{
//枚举的下标默认从0开始,0表示退出EXIT
EXIT,
ADD,
DEL,
SEARCH,
MODIFY,
SHOW,
SORT
};
switch (input)
{
case ADD:
AddContact(&con);
break;
case DEL:
DelContact(&con);
break;
case SEARCH:
SearchContact(&con);
break;
case MODIFY:
ModifyContact(&con);
break;
case SHOW:
ShowContact(&con);
break;
case SORT:
SortContact(&con);
break;
case EXIT:
SaveContact(&con);
DestroyContact(&con);
printf("退出通讯录\n");
break;
default:
printf("选择错误\n");
break;
}
通过枚举将case 选项与要实现的具体函数对应起来,更加符合逻辑
优点2:和#define定义的标识符比较枚举有类型检查,更加严谨
#define Mon 1
#define Tues 2
....
#define 定义的标识符是没有类型的,而enum枚举是有类型的(枚举类型)变量
当然这一点在C语言当中很难体现出来,在C++可以很好的显示出问题(C++对语法、类型的检查更为严格)
C++认为5是int类型 而d是Day枚举类型,无法将5赋值给Day(但是C语言中可以实现)
优点3:防止命名污染(封装)
将EXIT,ADD,DEL,SEARCH,MODIFY,SHOW,SORT进行封装
优点4:便于调试
优点5:使用方便,一次可以定义多个常量
#define 一次只能定义一个,而枚举可以定义多个
4 联合
4.1 联合的定义
联合也是一种特殊的自定义类型
这种类型定义的变量也包含一系列的成员,特征是这些成员公用同一块空间(所以联合也叫共用体)。
联合体中的c和i公用一块内存空间
根据联合的特点可以实现计算机大小端的判断
4.2 联合大小的计算
1、联合的大小至少是最大成员的大小。
2、当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。(vs的默认对齐数是8)