目录
1.结构体
1.1结构基本概念
“结构” struct ure• 通过多个同种/不同种的部件,组成一个设备• 每个部件本身都是 基础元件• 形成的结构具有更高层级的 实际含义1.2结构体
• 多个数据类型聚合形成一个 新数据类型• 每个成员描述该新类型的一个属性• 形成的新类型具有 新含义类比 商品:Item价格:float存货:int编号:long long名称:char[]struct Item { long long id; int count; float price; char name[20]; }; //1.声明结构体类型 struct Item a; //2.定义结构体变量 a.count = 10; a.price = 3.5f; printf("%.2f", a.price) //3.访问结构体变量的成员
1.3结构体声明与定义
1.3.1结构体声明vs定义
声明: declare与 定义 :define•声明declaration :让编译器知道 存在 这么一个事物(名字)• 定义 defination :让编译器实际去生成一个东西struct Item { long long id; int count; float price; char name[20]; }; //结构体声明 struct Item a; //结构体(变量)定义
1.3.2结构体声明与定义的几种形式
1. 先声明,后定义struct Student { char name[20]; long long id; int age; float score; }; //结构体声明 struct Item a; //结构体(变量)定义
2. 声明结构体类型同时定义变量struct Student { char name[20]; long long id; int age; float score; }a, b, c;//声明结构体类型同时定义变量 struct Item d;//结构体(变量)定义 b.age = 20; d.score = 85.5f;
typedef 关键字• typedef = type + define,意为为类型起别名• 每次都写 unsigned long long,很长很烦对吧• 没问题,给它起个 类型别名 即可,比如叫 uint64 挺好,简洁明了typedef unsigned long long uint64; uint64 a;//等价于unsigned long long a; a = 0xFFFFFFFFFFFFFFFF; printf("%ld", a);
• typedef 并不是宏定义替换!结构体声明与定义的几种形式: typedef 关键字3. 先声明结构体类型,然后为它起别名,再使用这个别名struct Student { char name[20]; long long id; int age; float score; };//结构体声明 typedef struct Student S;//给这个结构体类型起一个类型别名 S s1, s2;//使用这个别名定义变量 // 后续使用...
4. 声明结构体的同时给它起别名,使用这个别名typedef struct Student { char name[20]; long long id; int age; float score; } S;//声明结构体的同时起一个类型别名S S s1, s2;//使用这个别名定义变量 // 后续使用...
• 注意4.和2.有本质区别,4.本质是一个typedef语句,而2.本质是一个变量定义语句1.4例题
• Key 1 :结构体是一种自定义聚合数据类型,捏合了多个不同类型的成员,按名索引• Key 2 :结构体声明=>创造新数据类型;结构体定义=>创造新变量1.下面哪项不是C语言的基础数据类型( C)A.shortB.doubleC.structD.void分析:void指“空类型”,strruct 是自定义数据类型2.以下代码_______了一个_______ (B )A. 声明,变量B. 声明,数据类型C. 定义,变量D. 定义,数据类型struct ItemToSell { long id; int count; float price; };
• Key 3 :typedef 语句的作用是:为类型起 别名1. 有如右结构体声明和定义,其中,Item和tissue分别为( C)A. 变量,变量B. 数据类型,数据类型C. 数据类型,变量D. 变量,数据类型typedef struct ItemToSell { long id; int count; float price; } Item; Item milk,tissue;
2. 有如右结构体声明和定义,item 和 i1(A )A. 都是变量,类型相同B. 都是变量,类型不同C. item是数据类型/i1是变量D. item是变量/i1数据类型struct ItemToSell { long id; int count; float price; } item; typedef struct ItemToSell Item; Item i1,i2;
3. 有如下代码段,则 sizeof(kk) 的值为( C)A. 2B. 4C. 8D. 产生编译错误typedef unsigned long long uint64; uint64 kk = 0xFFFFFFFF;
1.5使用结构体
初始化和使用结构体变量的成员• 可以使用初始化列表 {...} 里的值 按顺序/按名字 为结构体的各个成员赋初值• 使用 点运算符 . 访问结构体的成员typedef struct Student { char name[20]; unsigned long long id; int age; float score; } S; S s1 = {"Mike", 2022196739, age : 19, score : 85.5f}; printf("%s, %ld, %d, %.1f", s1.name, s1.id, s1.age, s1.score);
输出:Mike, 2022196739, 19, 85.5
如果是这样呢? S s2 = {"Mike", 2022196739}; printf("%d, %.1f", s2.age, s2.score); 输出:0, 0.0
1.6结构体的大小
1.6.1引言
• 使用 sizeof 运算符获取结构体(变量)占据的内存大小• 原则上,一个结构体所占大小应当等于其所有数据成员大小之和• 但是凡提原则,必有例外!typedef struct Date { int year; int month; int day; } D; D day1 = {2022, 3, 10}, day2; printf("%d, %d, %d, %d", sizeof(struct Date), sizeof(D), sizeof(day1), sizeof(day2));
输出:12, 12, 12, 12
1.6.2内存对齐
• 理论上,下例中结构体 struct Date 的大小是 4 + 4 + 4 + 1 = 13字节• 但实测为16字节,原因是编译器会对结构体数据内容进行 内存对齐(提高效率)•通常是4字节对齐 即结构体大小为4的整数倍typedef struct Date { int year; int month; int date; char day; } D; D day1 = {2022, 3, 10,'4'}, day2 = {1949, 10, 1,'6'}; printf("%d, %d, %d, %d", sizeof(struct Date), sizeof(D), sizeof(day1), sizeof(day2));
输出:16, 16, 16, 16
1.7例题
• Key 1 :定义结构体变量的同时可使用初始化列表{...},按序提供数据成员的初始值• Key 2 :内存对齐:结构体 S 的大小 X 满足 X ≥ sizeof(S) 且 X = 4k, k ∈ {1, 2, ...}1. 将下列哪项填入(a) 不 能使结构体变量 stu 的年龄为10 (C )A. {“Mike” , {88.5f, 95.f, 100.f}, 10}B. {name : “Mike” , score : {88.5f, 95.f, 100.f}, age : 10}C. {name = “Mike” , score = {88.5f, 95.f, 100.f}, age = 10}D. {“Mike” , 88.5f, 95.f, 100.f, 10}分析:命名初始化结构体成员数据需要使用 XXX: YYY 形式,而非 XXX = YYYstruct Student { char name[13]; // 姓名 float score[3]; // 语数英三科成绩 int age; // 年龄 } stu = (a) ;
2. 接上题,sizeof(stu) 的值为( C)A. 28B. 29C. 32D. 36分析:stu各项数据成员大小之和为 13 + 3 * 4 + 4 = 29,首个大于等于29的4的倍数是322.联合体
2.1union
• 联合体(共用体),类似于结构体,但是存在本质区别:叠叠乐• 联合体是所有成员 共享 内存空间的结构体,可以想象成它们重叠在一起• 因此一个联合体的大小等同于其 最大成员 的大小类比:相当于吃饭时,几个手机叠放在一起typedef union UserAddress { AddressA a1; // 美国地址 大小=78 AddressB a2; // 中国地址 大小=96 AddressC a3; // 南极地址 大小=16 } Address; Address a; printf("%d, %d, %d", sizeof(union UserAddress), sizeof(Address), sizeof(a));
输出:96, 96, 96
2.2联合体的意义
• 为什么有了结构体还要有联合体?• 场景1:各个成员 互斥 ,同一时间只有一项生效• 场景2:同一个数据可以合起来解读,也可以 分开 多部分解读• 联合体在内存资源紧张的平台(如嵌入式)广泛使用union Integer { unsigned int n; unsigned short segment[2]; } a; a.n = 0xAABBCCDD; printf("%X, %X, %X", a.n, a.segment[0], a.segment[1]);
输出:AABBCCDD, CCDD, AABB 这不是字符串!是十六进制数值! 小端存储
2.3例题
• Key 1 :联合体各个数据成员共享空间,其大小为最大成员的大小1. 请写出下列值:union stud { short int num; char name[10]; float score[5]; double ave; } c;
sizeof(c):_20_______
typedef struct KK { short a[7]; char ch; } K; K k;
sizeof(k):_16______
typedef union ACHAR { char a; char b; char c; } C; C u;
sizeof(C):_____1____
• Key 2 :联合体各个数据成员共享空间,对任意成员的修改都会反应到所有数据成员上1. 运行在小端机器上,则该程序段输出的内容为(D )A. y, y, d, sB. a, y, d, sC. , , , aD. a, , ,typedef union Flexible { char ch[4]; short sh[2]; int n; } F; F f = {"yyds"}; f.n = 97; printf("%c, %c, %c, %c", f.ch[0], f.ch[1], f.ch[2], f.ch[3]);
分析:使用 {“yyds”} 初始化 f 时实际上为 f.ch (一个 char [4] 数组) 赋初值 “yyds” ,但由于联合体各个数据成员是重叠的,即 f.n 和 f.ch 本质是相同 的4个字节,所以 f.n = 97 实际上将该联合体的4个字节修改为整数97的二进制值。由于运行于小端机器,即整数的高位存放于高字节,低位存 放于低字节,97的二进制值中有效部分为低位,存放于这4个字节的最前端的1个字节,即ch[0],另3个字节皆为0值,对应’\0’ ,即空字符3.枚举类型
3.1enumerate
• 有些类型的取值天然就是 有限 集合:• 月份、星期、物品种类、用户角色类型...• 在程序中通过声明一个枚举类型来表示一个有限取值的概念enum Day {Mon = 1, Tue, Wed, Thur, Fri, Sat, Sun}; enum Day day = Tue; printf("%d, %d", day, Thur);
输出:2, 4
• 一个枚举类型的取值限定为有限个 整数不变量• 由该类型定义的变量只能取这些不变量中的其中一个值3.2枚举变量的整数值
• 枚举类型的整数不变量取值可以手动指定,不指定则默认递增enum XXX {A = 1, B, C, D}; printf("%d, %d, %d, %d", A, B, C, D); 输出:1, 2, 3, 4
enum XXX {A = 1, B = 7, C, D}; printf("%d, %d, %d, %d", A, B, C, D); 输出:1, 7, 8, 9
enum XXX {A, B, C, D}; printf("%d, %d, %d, %d", A, B, C, D); 输出:0, 1, 2, 3
enum XXX {A = 1, B, C = 7, D}; printf("%d, %d, %d, %d", A, B, C, D); 输出:1, 2, 7, 8
enum XXX {A = -1, B, C, D}; printf("%d, %d, %d, %d", A, B, C, D); 输出:-1, 0, 1, 2
enum XXX {A = 1, B, C = -1, D, E}; printf("%d, %d, %d, %d, %d", A, B, C, D, E); 输出:1, 2,-1, 0, 1 但是这种写法考试只是单纯考知识点,现实中根本不会写这种代码,枚举的值应该不能有相同的,不然违背枚举的初衷
3.3例题
• Key 1 :枚举类型enum声明一个有限的命名取值(整数)集合,限定其变量取值1. 有如下代码段,若用户输入为1,则输出为(B )A. ERROR: 1B. WARN: 1C. WARN: WARND. ERROR: ERRORenum LogType {INFO = 0, WARN, ERROR}; typedef enum LogType T; T t; do { scanf("%d", &t); } while (t < INFO || t > ERROR); if (INFO == t) printf("INFO: %d", t); else if (WARN == t) printf("WARN: %d", t); else if (ERROR == t) printf("ERROR: %d", t);
2. 接上题,sizeof(t) 的值为( D)A. 1B. 2C. 3D. 4