前言:本章主要内容是C语言自定义类型中的结构体、枚举和联合。
【C语言重难点进阶|自定义类型详解(结构体+枚举+联合)【C进阶】】
文章目录
- 结构体
-
-
- 结构
-
- 结构的声明
- 例如描述一个学生:
- 特殊的声明(声明结构也可以不完全声明)
-
- 例如(省略结构体标签tag):
- 结构的自引用
-
-
- 例如:
-
- 结构体变量的定义和初始化
-
-
- 1.声明类型的同时定义变量p1
- 2.定义结构体变量p2
- 3.初始化:定义变量的同时赋初值。
-
- 3.1
- 3.2
- 4.结构体嵌套初始化
-
- 4.1
- 4.2
-
- 结构体内存对齐
-
- 如何计算?首先得掌握结构体的对齐规则:
-
- 例题,计算下列结构体的大小。
- 为什么存在内存对齐?
- 那在设计结构体的时候,我们既要满足内存对齐,又想节省空间,如何做到?
-
- 例题:
- 修改默认对齐数
-
- 1.设置默认对齐数
- 2.取消设置默认对齐数,还原为默认
- 例子
- 结构体传参
-
- 例题(结构体传参和结构体地址传参)
- 位段
-
- 什么是位段?
-
- 比如:
- 位段的内存分配原则
-
- 例子:
- 思考下上例中空间是如何开辟的?
- 位段的跨平台问题
- 位段的应用
-
- 枚举
-
-
- 枚举类型的定义
-
- 星期、性别、颜色枚举类型举例
- 枚举常量都是有值得,默认从0开始,一次递增1,当然也可以在定义的时候赋初值。例如:
- 枚举的优点(明明已经有#define定义常量,为什么非要使用枚举?)
- 枚举的使用
-
- 例子1:这样给clr赋值5可以吗?
- 例子2:更加直观的显示所要实现算法的功能,而非简单的1、2、3、4.
-
- 联合(共用体)
-
-
-
- 联合类型的声明、定义和联合类型变量大小计算:
- 联合的特点
- 判断大小端存储
-
- 例题:若位顺序类似小端,低地址在低处,所以39是低地址,在低位,38在高位,结果就是3839;若类似大端存储,则结果是3938
- 联合大小的计算
-
- 例题:
-
-
结构体 结构
定义:结构是一些值的集合,这些值成为成员变量。结构的每个成员可以是不同类型的变量。结构的声明
struct tag
{
member - list;
}variable-list;
例如描述一个学生:
struct stu
{
char name[30];
//名字
int age;
//年龄
char sex[5];
//性别
};
特殊的声明(声明结构也可以不完全声明) 例如(省略结构体标签tag):
struct
{
int a;
char b;
float c;
}a;
struct
{
int a;
char b;
float c;
}a[30],*p;
注意:编译器会把上面两个声明当成完全不同的两个类型,所以使用 p=&x 非法。结构的自引用
例如:
struct Node
{
int data;
struct Node* next;
};
结构体变量的定义和初始化
1.声明类型的同时定义变量p1
struct Point
{
int x;
int y;
}p1;
2.定义结构体变量p2
struct Point p2;
3.初始化:定义变量的同时赋初值。 3.1
struct Point p3 = { x , y };
3.2
struct Stu
{
char name[20];
int age;
};
struct Stu s = { "zhangsan",20 };
4.结构体嵌套初始化 4.1
struct Node
{
int data;
struct Point p;
struct Node* nest;
}n1 = { 10,{4,5},NULL };
4.2
struct Node n2 = { 20,{5,6},NULL };
结构体内存对齐
掌握了结构体的基本使用后,接下来介绍下如何计算结构体的大小,这就涉及到了一个热门考点:结构体内存对齐。如何计算?首先得掌握结构体的对齐规则:
1.第一个成员在与结构体变量偏移量为0的地址处。例题,计算下列结构体的大小。
2.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数=编译器默认的一个对齐数与该成员大小的较小值。
VS中的默认值是8
3.结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
struct S
{
char c1;
int i;
char c2;
};
struct S2
{
char c1;
int i;
double d;
//8
};
struct S3
{
char c1;
char c2;
int i;
};
struct S4
{
double d;
char c;
int i;
};
struct S5
{
char c1;
struct S4 s4;
double d;
};
#include
int main()
{
struct S s = {0};
struct S2 s2 = { 0 };
struct S3 s3 = { 0 };
struct S4 s4 = { 0 };
struct S5 s5 = { 0 };
printf("%d\n", sizeof(s5));
//32
printf("%d\n", sizeof(s));
//12
printf("%d\n", sizeof(s2));
//16
printf("%d\n", sizeof(s3));
//8
printf("%d\n", sizeof(s4));
//16
return 0;
}
文章图片
注释:
以S为例,画下S的图示。其余类似,大家可以自己动手画图分析一波!
文章图片
S的c1占用一个字节,浪费三个字节,int从下标4(第五个)开始,占用四个字节,c2占用一个字节,浪费3个字节,累计1+3+4+1+3=12;为什么存在内存对齐?
S2的c1占用一个字节,浪费三个字节,int从下标4(第五个)开始,占用四个字节,d占用8个字节,累计1+3+4+8=16;
S3的c1占用一个字节,c2占用一个字节,然后浪费两个字节,int从下标4(第五个)开始,占用四个字节,累计1+1+2+4=8;
S4的d占用8个字节,c占用一个字节,然后浪费三个字节,int从下标12(第13个)开始,占用四个字节,累计8+1+3+4=16;
S5的c1占用1个字节,然后浪费7个字节,struct s4从下标8(第九个)开始(此时默认对齐数是8),占用16个字节,double d从下标24(第25个)开始,占用8个字节,累计1+7+16+8=32;
文章图片
总结:结构体的内存对齐是拿空间换取时间那在设计结构体的时候,我们既要满足内存对齐,又想节省空间,如何做到?
答:让占用空间小的成员尽量集中在一起例题:
struct s1
{
char c1;
int i;
char c2;
};
struct s2
{
char c1;
char c2;
int i;
};
注释:上例中s1和s2类型的成员一模一样,但是s1占了12字节,s2占8字节,s2更具备优势。修改默认对齐数 1.设置默认对齐数
#pragma pack(n)//默认对齐数为n
2.取消设置默认对齐数,还原为默认
#pragma pack()//取消默认对齐数
例子
#pragma pack(2)//默认对齐数为2
struct S
{
char c1;
//0
int i;
//
char c2;
//
};
#pragma pack()//取消默认对齐数#pragma pack(1)//默认对齐数为1
struct S
{
char c1;
//1 1 1
int i;
//4 1 1
char c2;
//1 1 1
};
#pragma pack()//取消默认对齐数int main()
{
printf("%d\n", sizeof(struct S));
return 0;
}
注释:结构在对齐方式不合适的时候,我们可以自己更改默认对齐数。结构体传参 例题(结构体传参和结构体地址传参)
struct S
{
int data[1000];
int num;
};
struct S s = { {1,2,3,4},1000 };
void print1(struct S s)//结构体传参
{
printf("%d\n", s.num);
}
void print2(struct S* ps)//结构体地址传参
{
printf("%d\n", ps->num);
}
int main()
{
print1(s);
print2(&s);
return 0;
}
注释:对比上面的print1和print2函数,print1的缺点很明显:在函数传参过程中,参数需要压栈,会有时间和空间上的系统开销。如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销比较大,会导致性能下降。
总的来说,结构体传参的时候,传结构体的地址(指针大小固定4/8)。位段
什么是位段? 位段的声明和结构是类似的,注意两点不同:
1.位段的成员必须是int、unsigned int或signed int。比如:
2.位段的成员名后边有一个冒号和一个数字。
struct A
{
//4个字节 - 32bit
int _a : 2;
//_a 成员占2个bit位
int _b : 5;
//_b 成员占5个bit位
int _c : 10;
//_c 成员占10个bit位
//剩余15个bit
//4个字节 - 32bit
int _d : 30;
//_b 成员占30个bit位
};
int main()
{
printf("%d\n", sizeof(struct A));
//8 return 0;
}
注释:A就是一个位段类型,大小是8字节。位段的内存分配原则
1.位段的成员可以是int、unsigned int和signed int或者是char(属于整形家族)类型。例子:
2.位段的空间上是按照需要以4个字节(int)或者1个字节(char)的方式来开辟。
3.位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。
struct S
{
char a : 3;
char b : 4;
char c : 5;
char d : 4;
};
int main()
{
struct S s = { 0 };
s.a = 10;
s.b = 12;
s.c = 3;
s.d = 4;
return 0;
}
思考下上例中空间是如何开辟的? 答:
文章图片
位段的跨平台问题
1.int位段被当成有符号数还是无符号数是不确定的。总结:
2.位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机器会出现问题)。
3.位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
4.当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余位段还是继续利用剩余位段,这点不确定。
跟结构相比,位段可以达到同样的效果,还可以很好的节省空间,但是存在跨平台的问题。位段的应用
在计算机网络的信息传输过程中,减小数据包的大小。
文章图片
枚举
顾名思义就是把可能的取值一一列举。
比如:枚举类型的定义
月份有12个月,可以一一列举。
一周有七天,可以一一列举。
性别有男、女,可以一一列举。
颜色也可以一一列举。
这些例子里就可以使用枚举。
星期、性别、颜色枚举类型举例
enum Day
{
Mon,
Tues,
Wed,
Thur,
Fri,
Sat,
Sun
};
enum Sex
{
MALE,
FEMALE,
SECRET
};
enum Color
{
RED,
GREEN,
BLUE
};
注释:以上定义的enmu Day,enum Sex,enum Color都是枚举类型。{}中的内容是枚举类型的可能取值,也叫枚举常量。枚举常量都是有值得,默认从0开始,一次递增1,当然也可以在定义的时候赋初值。例如:
enum Color
{
RED=1,
GREEN=2,
BLUE=4
};
枚举的优点(明明已经有#define定义常量,为什么非要使用枚举?)
1.增加代码的可读性和可维护性枚举的使用 例子1:这样给clr赋值5可以吗?
2.和#define定义的标识符相比枚举有类型检查,更严谨
3.防止命名污染(封装)
4.便于调试
5.使用方便,一次可以定义多个变量
enum Color
{
RED=1,
GREEN=2,
BLUE=4
};
enum Color clr = GREEN;
clr = 5;
答:不可以,枚举变量一经过定义赋值,不可再改变。例子2:更加直观的显示所要实现算法的功能,而非简单的1、2、3、4.
void menu()
{
printf("*****************************\n");
printf("****1. add2. sub*****\n");
printf("****3. mul4. div*****\n");
printf("****0. exit*****\n");
printf("*****************************\n");
}enum Option
{
EXIT,//0
ADD,//1
SUB,//2
MUL,//3
DIV//4
};
int main()
{
int input = 0;
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case ADD:
break;
case SUB:
break;
case MUL:
break;
case DIV:
break;
case EXIT:
break;
default:
break;
}
} while (input);
return 0;
}
联合(共用体)
联合也是一种特殊的自定义类型,这种类型定义的变量也包含一系列的成员,特征是这些成员共用同一块空间(所以叫做共用体).联合类型的声明、定义和联合类型变量大小计算:
union Un
{
short s[7];
int n;
};
Union Un un;
int main()
{
printf("%d\n", sizeof(union Un));
return 0;
}
注释:结构体向int对齐,7个short一共是14字节,对齐后是16字节。n是单独的4字节,由于是union,所以n与s共用空间,只取最长的元素,故占用16字节。联合的特点
联合的成员是共用同一块内存空间,这样一个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)判断大小端存储 例题:若位顺序类似小端,低地址在低处,所以39是低地址,在低位,38在高位,结果就是3839;若类似大端存储,则结果是3938
int main()
{
union
{
short k;
char i[2];
}*s, a;
s = &a;
s->i[0] = 0x39;
s->i[1] = 0x38;
printf("%x\n", a.k);
return 0;
}
文章图片
注释:由输出结果可知位顺序类似小端,低地址在低处,所以39是低地址,在低位,38在高位,所以是3839。联合大小的计算
1.联合的大小至少是最大成员的大小。例题:
2.当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。
int main()
{
union Un1
{
char c[5];
int i;
};
union Un2
{
short c[7];
int i;
};
printf("%d\n", sizeof(union Un1));
printf("%d\n", sizeof(union Un2));
return 0;
}
文章图片
注释:Un1中,char c[5]至少占用6个字节,最大对齐数4,取最大对齐数的整数倍,结果为8;Un2中,short c[7]至少占用16个字节,最大对齐数4,取最大对齐数的整数倍,恰好结果为16.自定义类型(结构体+枚举+联合)部分到此介绍结束了,感谢您的阅读!!!如果内容对你有帮助的话,记得给我三连(点赞、收藏、关注)——做个手有余香的人。
推荐阅读
- C进阶|C语言深度解析之六(自定义类型详解(结构体+枚举+联合))
- C语言|C语言--自定义类型详解(结构体+枚举+联合)
- C语言初阶|自定义类型详解(结构体+枚举+联合)
- 【C语言进阶】自定义类型详解(结构体+枚举+联合)
- html|自定义类型~结构体~位段~枚举~联合~超详解~一遍就会
- 小程序|C语言实现通讯录
- C语言笔记|c语言入门笔记
- 初学者能学会的数据结构与算法|数算部分-----第一节----算法的时空复杂度
- python|Python爬取网易云音乐网易云音乐歌手歌曲和歌单,并下载到本地