C语言自定义类型全解析

📅2026/7/14 7:11:34 👁️次浏览
C语言自定义类型全解析
C 语言自定义类型学习笔记结构体、联合体与枚举最近系统学习了 C 语言自定义类型三大核心结构体、联合体、枚举这是 C 语言从基础语法走向实际开发的关键知识点。第一部分结构体struct- 最常用的自定义类型结构体是把不同类型的数据打包在一起用来描述一个完整的对象学生、商品、节点等是 C 语言数据封装的基础。1. 结构体的 3 种声明方式 typedef 重命名声明时末尾分号绝对不能丢这是我最开始反复踩的坑#include stdio.h // 1. 标准声明最常用 struct Student { // 成员变量不同类型任意组合 char name[20]; // 姓名 int age; // 年龄 float score; // 成绩 }; // 2. 声明时直接定义变量 struct Teacher { char name[20]; int id; } t1, t2; // 直接定义全局变量t1、t2 // 3. typedef重命名简化写法后续不用写struct typedef struct Book { char bookName[30]; float price; } Book; // 以后直接用Book代替struct Book2. 结构体的 3 种初始化方式推荐指定成员初始化int main() { // 方式1按成员顺序初始化 struct Student s1 {张三, 18, 95.5}; // 方式2指定成员初始化C99标准最灵活 struct Student s2 {.score88.0, .name李四, .age20}; // 方式3typedef重命名后初始化 Book b1 {《C语言从入门到精通》, 59.9}; // 打印验证 printf(s1%s %d %.1f\n, s1.name, s1.age, s1.score); printf(s2%s %d %.1f\n, s2.name, s2.age, s2.score); printf(书籍%s %.1f\n, b1.bookName, b1.price); return 0; }3. 匿名结构体慎用省略结构体名只能用一次编译器会把相同成员的匿名结构体当成不同类型// 匿名结构体 struct { int a; char b; } x; // 只能定义变量x无法重复使用 // 错误两个匿名结构体是不同类型不能赋值 // struct {int a; char b;} y x;4. 结构体自引用链表核心错误直接包含自身变量 → 内存无限嵌套正确包含自身指针→ 指针大小固定4/8 字节// 正确的结构体自引用单链表节点 typedef struct Node { int data; // 数据域 struct Node* next; // 指针域指向下一个节点 } Node; // 实战创建3个节点的简单链表 int main() { Node n1 {10, NULL}; Node n2 {20, NULL}; Node n3 {30, NULL}; // 建立连接 n1.next n2; n2.next n3; // 遍历链表 Node* p n1; while (p ! NULL) { printf(%d , p-data); p p-next; } return 0; } // 输出10 20 305. 核心难点结构体内存对齐5.1对齐规则VS 默认对齐数 8第一个成员 → 偏移量 0后续成员 → 对齐到min(默认对齐数, 成员大小)的整数倍总大小 → 最大对齐数的整数倍嵌套结构体 → 对齐到自身最大对齐数5.2代码示例计算结构体大小// 示例1 struct S1 { char c1; // 1字节 int i; // 4字节 char c2; // 1字节 }; // 大小计算1(对齐到4)41(对齐到4) 12字节 // 示例2优化成员顺序小成员集中 struct S2 { char c1; char c2; int i; }; // 大小计算11(对齐到4)4 8字节 // 示例3嵌套结构体 struct S3 { char c; struct S2 s2; double d; }; // 大小1 7(填充) 8 8 24字节 // 测试代码 int main() { printf(S1大小%d\n, sizeof(struct S1)); // 12 printf(S2大小%d\n, sizeof(struct S2)); // 8 printf(S3大小%d\n, sizeof(struct S3)); // 24 return 0; }5.3修改默认对齐数#pragma pack(1) // 设置默认对齐数为1无内存填充 struct S4 { char c1; int i; }; #pragma pack() // 恢复默认对齐数 // sizeof(S4) 56. 结构体数组 结构体指针// 结构体数组存储多个学生 struct Student arr[3] { {小明, 17, 90.0}, {小红, 18, 92.5}, {小刚, 19, 85.0} }; // 结构体指针访问成员两种方式 int main() { struct Student s {小王, 20, 88.0}; struct Student* p s; // 方式1(*指针).成员 printf(%s , (*p).name); // 方式2指针-成员最常用 printf(%d %.1f\n, p-age, p-score); return 0; }7. 结构体传参必传指针传值会拷贝整个结构体效率极低传指针仅 4/8 字节更高效struct Student { char name[20]; int age; }; //不推荐传值拷贝大结构体开销大 void print1(struct Student s) { printf(%s %d\n, s.name, s.age); } //推荐传指针加const防止修改 void print2(const struct Student* s) { printf(%s %d\n, s-name, s-age); } int main() { struct Student s {小李, 21}; print1(s); print2(s); return 0; }8. 结构体进阶位段节省内存位段 结构体的特殊形式按 bit 分配内存用于网络协议、硬件编程。规则成员必须是整型:后数字表示占用 bit 数// 位段声明 struct BitSeg { int a : 2; // a占2bit int b : 5; // b占5bit int c : 10; // c占10bit int d : 30; // d占30bit }; int main() { struct BitSeg bs; bs.a 3; // 2bit最大存3 bs.b 20; // 5bit最大存31 printf(位段大小%d\n, sizeof(bs)); // 8字节VS分配规则 return 0; }第二部分联合体union联合体也叫共用体所有成员共用同一块内存大小 最大成员大小对齐后。1. 核心特性内存共享赋值互相覆盖#include stdio.h // 联合体声明 union Data { int i; char c; float f; }; int main() { union Data data; data.i 10; printf(i %d\n, data.i); // 10 data.c A; // 覆盖低字节 printf(c %c\n, data.c); // A printf(i %d\n, data.i); // 65内存被覆盖 return 0; }2. 联合体大小计算对齐规则至少 最大成员大小必须是最大对齐数的整数倍// 示例1 union Un1 { char c[5]; // 5字节对齐数1 int i; // 4字节对齐数4 }; // 大小85对齐到4的整数倍 // 示例2 union Un2 { short s[7]; // 14字节对齐数2 int i; // 4字节对齐数4 }; // 大小1614对齐到4的整数倍 int main() { printf(Un1%d\n, sizeof(union Un1)); // 8 printf(Un2%d\n, sizeof(union Un2)); // 16 return 0; }3. 经典实战判断机器大小端小端低字节存低地址大端高字节存低地址// 联合体判断大小端极简代码 int check_sys() { union { int i; char c; } un; un.i 1; // 返回1小端0大端 return un.c; } int main() { if (check_sys()) { printf(小端存储\n); } else { printf(大端存储\n); } return 0; }4. 多类型数据存储节省内存// 礼品兑换同一时间只存一种礼品属性 typedef struct Gift { int type; // 0图书1杯子2衣服 // 联合体封装特殊属性 union { struct { char author[20]; } book; struct { char color[10]; } cup; struct { int size; } clothes; } attr; } Gift; int main() { Gift g; g.type 0; g.attr.book.author 张三; return 0; }第三部分枚举enum- 优雅定义常量枚举 把有限的取值一一列举替代#define代码更规范、有类型检查。1. 枚举声明 赋值规则默认从 0 开始依次 1可手动赋值后续自动递增// 1. 默认赋值RED0, GREEN1, BLUE2 enum Color { RED, GREEN, BLUE }; // 2. 手动赋值MON1, TUE2, WED3... enum Week { MON1, TUE, WED, THU, FRI, SAT, SUN };2. 枚举完整使用示例#include stdio.h enum Week {MON1,TUE,WED,THU,FRI,SAT,SUN}; int main() { enum Week today WED; printf(今天是星期%d\n, today); // 3 // 枚举做分支判断代码可读性拉满 if (today SAT || today SUN) { printf(周末休息\n); } else { printf(工作日\n); } return 0; }3. 枚举 VS #define为什么用枚举// #define 无类型、无作用域、调试不显示 #define RED 1 #define GREEN 2 #define BLUE 3 // 枚举有类型、有作用域、可调试、一次定义多个 enum Color {cRED1, cGREEN, cBLUE}; // 枚举传参类型安全#define做不到 void printColor(enum Color c) { printf(%d\n, c); }第四部分综合实战整合所有知识点4.1 简易学生信息管理系统#include stdio.h // 结构体封装学生 typedef struct Stu { char name[20]; int age; float score; } Stu; // 枚举定义菜单 enum Menu {EXIT, ADD, SHOW}; // 显示菜单 void menu() { printf(1.添加学生 2.显示学生 0.退出\n); } // 添加学生传指针 void addStu(Stu* s, int i) { printf(输入第%d个学生信息\n, i1); scanf(%s %d %f, s-name, s-age, s-score); } // 显示学生 void showStu(Stu* s, int n) { for (int i0; in; i) { printf(%s %d %.1f\n, s[i].name, s[i].age, s[i].score); } } int main() { Stu arr[50]; int n 0; int choice; while (1) { menu(); scanf(%d, choice); switch (choice) { case ADD: addStu(arr[n], n); n; break; case SHOW: showStu(arr, n); break; case EXIT: return 0; default: printf(输入错误\n); } } }我的学习总结结构体封装不同数据重点掌握内存对齐、传指针、自引用位段用于节省内存。联合体成员共享内存大小 最大成员对齐后大小端判断是必考用法。枚举定义有限常量比#define更安全、可读性更高适合分支判断。开发原则结构体传参必传指针成员顺序优化节省内存优先用枚举代替宏常量。