C语言第22讲
目录
自定义类型:结构体
结构的自引用
结构体在内存中开辟空间的流程
结构体实现位段
位段的内存分配:
自定义类型:联合
自定义类型:枚举
自定义类型:结构体
结构体的初始化和使用:
#include <stdio.h>struct Stu{char name[20];//名字int age;//年龄 char sex[5];//性别 char id[20];//学号 };int main(){//按照结构体成员的顺序初始化struct Stu s = { "张三", 20, "男", "20230818001" };printf("name: %s\n", s.name);printf("age : %d\n", s.age);printf("sex : %s\n", s.sex);printf("id : %s\n", s.id);//按照指定的顺序初始化struct Stu s2 = { .age = 18, .name = "lisi", .id = "20230818002", .sex = "⼥" };printf("name: %s\n", s2.name);printf("age : %d\n", s2.age);printf("sex : %s\n", s2.sex);printf("id : %s\n", s2.id);return 0;}
匿名结构体:当我们去掉其结构体名称时就是匿名结构体
这类特殊结构体变量的创建需要即使创建
下图为一错误示范
虽然我们知道是同类型,但是因为是匿名结构体,所以编译器无法分辨所以会报错
下图为拖裤子放屁炒作:
将匿名数组重命名,导致匿名数组不在值使用一次
结构的自引用
数组是在内存中连续开辟空间存放内容也称顺序表
链表:
数据在空间中随机存放但是,每个数据都有下一个数据的地址,导致可以找寻到下一个数据内容。如图这般:
结构体的自引用功能和上述类似
代码如下:
struct STU
{char name[20];struct STU* next;
};
自己能引用和自己同类型的对象(匿名结构体不能自引用)
结构体在内存中开辟空间的流程
结构体对齐内存:
问题:下方代码运行结果
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>struct S
{char a;int b;char c;
}s = {0};int main()
{printf("%zd",sizeof(s)); // 12return 0;
}
对齐规则:
1.结构体第一个成员名对齐到结构体变量起始位置偏移量为0的地址处
2.其它变量要对齐到某个数字(对齐数)的整数倍的地址处
对齐数:编译器默认一个对齐数与该成员变量内存,中较小的值
VS默认为 8
linux 中gcc 没有对齐数,对齐数是成员自身大小
在上图中我们得到了最终内存大小为9,但还有最后一步
3.结构体总大小必须为最大对齐数的整体倍数
int b > char a 和c 为4 所以最终为 12
问题1:以下代码运行结果
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>struct S
{char c1;char c2;int a;
}s = {0};int main()
{printf("%zd",sizeof(s));return 0;
}
如果嵌套了结构体的情况,嵌套的结构体成员对齐到自己的成员中最大对齐数的整数倍处,结构体的整体大小就是所有对齐数中最大的倍数(包含被嵌套的结构体中的成员对齐数)
问题2:以下代码运行结果
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>struct S
{char c1;char c2;double a;
};struct M
{int a;struct S s;char c;
};int main()
{printf("%zd",sizeof(struct M)); // 32return 0;
}
问题3:以下代码运行结果
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>struct S
{int arr[7];char c2;double a;
};int main()
{printf("%zd", sizeof(struct S));return 0;
}
数组按元素对齐一次就行,后面按顺序排列
让占用空间小的成员尽量集中在一起可以做到节约空间
实际上我们可以更改默认对齐数
利用 #pragma
#pragma 的使用:
// 修改默认对齐数为1
#pragma pack(1) // 推荐设置为 2的次方// 取消设置,还原默认
#pragma pack()
结构体实现位段
当写代码时,一个 int 类型的变量只是接受 0 和1 两个参数时,只需要1个bit位就能解决时,就可以使用位段,减少内存的消耗
位段的实现:
// 结构体
struct S
{int a;char c1;char c2;
};
// 位段
struct M
{int a:8;char c1:2;char c2:2;
};
位段和结构体的差距只在变量和冒号之间多了个 分号和数字
数字的意义表示 需要 多少 的内存 大小为 bit 位
这里说明,位段设计很多不确定因素,位段是不跨平台的,注重可抑制的程序应该避免使用位段
位段的内存分配:
问题:以下代码运行结果是什么
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>struct S
{char a : 3;char b : 4;char c : 5;char d : 6;
};int main()
{printf("%zd", sizeof(struct S));return 0;
}
运行结果:
由于C语言没有规定位段的使用,所以会存在两处分歧
1.单个字节是从哪里开始使用
2.剩余空间不足下一个成员使用,是浪费还是给下一个成员用
上题在VS的存储逻辑如下图:
位段成员没有地址
不能对位段成员使用&操作符,这样就不能使用scanf直接给位段成员输入值了,只能通过先输入存放在一个变量中,然后赋值给位段成员
只能在结构体中使用位段
自定义类型:联合
联合体关键词 union
编译器只为最大的成员分配足够的内存空间。联合体的特点是所有成员公用一块空间。所以联合体也叫:共用体
下方代码能侧面验证使用同一块空间:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>union UN
{char c;int i;
};int main()
{union UN un = { 0 };printf("%zd\n", sizeof(un));printf("%p\n", &un);printf("%p\n", &un.c);printf("%p\n", &un.i);return 0;
}
问题:以下代码运行最终结果是什么
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>union UN
{char ch[5];int i;
};int main()
{union UN un = { 0 };printf("%zd\n", sizeof(un));return 0;
}
字符数组 5(1) 和整形 4 共用同一块空间,但是也遵循总内存是最大对齐数的倍数
4 > 1 最大对齐数为4
4*1 < 5 < 2*4 最终总空间大小为4
问题:⽐如,我们要搞⼀个活动,要上线⼀个礼品兑换单,礼品兑换单中有三种商品:图书、杯⼦、衬衫。 每⼀种商品都有:库存量、价格、商品类型和商品类型相关的其他信息。
·图书:书名、作者、⻚数
·杯⼦:设计
·衬衫:设计、可选颜⾊、可选尺⼨
我们可以将所有元素归纳到结构体中,但是由于各个商品的特殊性,会导致内存空间浪费,所以可利用上方结构体将每个商品的特殊元素,规整到一个空间中
代码如下:
struct gift_list
{ // 公共属性int stock_num; // 库存量double price; // 定价int item_type; // 商品类型// 特殊属性union{struct{char title[20]; // 书名 char author[20]; // 作者int num_pages; // 页数}book;struct{char designe[30]; // 设计}mug;struct{char designe[30]; // 设计int colors; // 颜色int sizes; // 尺寸};};
};
问题:设计一个判断大小端的程序
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>union S
{char a;int i;
}s;int mian()
{s.i = 1;printf("%d", (int)s.a);return 0;
}
自定义类型:枚举
枚举的关键词 enum
枚举的使用:
enum Day//星期
{Mon, // 0Tues, // 1Wed, // 2Thur, // 3Fri, // 4Sat, // 5Sun // 6
};
int main()
{enum Day d = Sun;return 0;
}
后方为逗号
enum Day 是类型 Mon,Tues,Wed,Thur,Fri,Sat,Sun是他的元素
char 的元素是 ASCII表 枚举的元素也类似,实际 Mon为0后续元素依次+1
如果给莫个元素赋值,前方不变,后方延赋值+1依次下去
enum Day//星期
{Mon, // 0Tues, // 1Wed = 5, // 5Thur, // 6Fri, // 7Sat, // 8Sun // 9
};
枚举常量遵循作用域规则