c语言项目驱动学习--实例化(图书管理)--002-代码对比

发布时间:2026/6/30 3:27:07

c语言项目驱动学习--实例化(图书管理)--002-代码对比 V2 完整逐行详细注释版 V1/V2 版本差异对比一、带全覆盖注释完整代码c运行// // Created by Administrator on 2026/6/28. // /** * * 阶段V2指针深化版 - 动态内存管理 * * 知识点结构体指针、动态内存分配malloc、内存扩容realloc、内存释放free、堆内存管理 * 功能书架支持动态扩容不再固定最大100本理论可存放大量图书受物理内存限制 * 版本迭代说明基于V1结构体数组版本升级解决静态数组容量固定的缺陷 * */ #include stdio.h #include string.h #include stdlib.h // malloc/realloc/free/exit 动态内存函数依赖头文件 // // 1. 宏定义字符串长度限制与V1保持一致 // #define MAX_NAME 100 #define MAX_AUTHOR 50 #define MAX_ISBN 20 // // 2. 图书结构体完全复用V1结构无改动 // struct Book { int id; // 图书唯一ID char name[MAX_NAME]; // 书名 char author[MAX_AUTHOR];// 作者 char isbn[MAX_ISBN]; // ISBN编号 int stock; // 总库存 int borrowed; // 已借出数量 void* borrowHistory; // 预留字段V3实现借阅记录链表 }; // // 3. 全局数据V2重大改动静态数组改为动态堆内存指针 // struct Book* library NULL; // 指向堆上动态结构体数组的指针初始为空 int bookCount 0; // 当前实际存储图书数量 int maxBooks 10; // 当前动态数组总容量初始分配10本空间 int nextId 1001; // 自增图书ID和V1逻辑一致 // // 4. 函数声明新增3个内存管理专属函数 // void initLibrary(); // 初始化动态书架分配初始堆内存 void destroyLibrary(); // 程序退出时释放堆内存防止内存泄漏 void ensureCapacity(); // 容量检测不足时自动翻倍扩容核心新增函数 void addBook(const char* name, const char* author, const char* isbn, int stock); struct Book* findBookById(int id); int findBookIndexById(int id); void borrowBook(int id); void returnBook(int id); void listAllBooks(); void deleteBook(int id); void showMenu(); // // 5. 主程序入口 // int main() { // V2新增程序启动先初始化动态内存书架 initLibrary(); // 预置3本测试图书逻辑与V1完全一致 addBook(C程序设计语言, Kernighan, 978-7-111-00101-0, 5); addBook(数据结构与算法, Weiss, 978-7-111-00202-0, 3); addBook(深入理解计算机系统, Bryant, 978-7-111-00555-0, 2); int choice; // 循环菜单 while (1) { showMenu(); scanf(%d, choice); if (choice 1) { // 1.添加图书输入逻辑不变 char name[MAX_NAME], author[MAX_AUTHOR], isbn[MAX_ISBN]; int stock; printf(请输入书名); scanf(%s, name); printf(请输入作者); scanf(%s, author); printf(请输入ISBN); scanf(%s, isbn); printf(请输入库存数量); scanf(%d, stock); addBook(name, author, isbn, stock); } else if (choice 2) { // 2.借书 int id; printf(请输入图书ID); scanf(%d, id); borrowBook(id); } else if (choice 3) { // 3.还书 int id; printf(请输入图书ID); scanf(%d, id); returnBook(id); } else if (choice 4) { // 4.按ID查询单本图书 int id; printf(请输入图书ID); scanf(%d, id); struct Book* b findBookById(id); if (b ! NULL) { printf(\n找到图书\n); printf( ID: %d\n, b-id); printf( 书名: %s\n, b-name); printf( 作者: %s\n, b-author); printf( ISBN: %s\n, b-isbn); printf( 库存: %d已借出: %d可借: %d\n, b-stock, b-borrowed, b-stock - b-borrowed); } else { printf(未找到ID为 %d 的图书\n, id); } } else if (choice 5) { // 5.列出全部图书 listAllBooks(); } else if (choice 6) { // 6.删除图书 int id; printf(请输入要删除的图书ID); scanf(%d, id); deleteBook(id); } else if (choice 7) { // 7.退出菜单循环 break; } else { // 非法输入提示 printf(无效选择请重新输入\n); } } // V2新增程序退出前释放堆内存避免内存泄漏 destroyLibrary(); printf(\n感谢使用图书管理系统再见\n); return 0; } // // 6. 函数实现新增内存管理3个核心函数原有业务函数适配动态指针 // /** * initLibrary初始化动态书架 * 作用程序启动时malloc分配初始堆内存创建动态数组 * 异常处理内存分配失败直接退出程序 */ void initLibrary() { // malloc分配 maxBooks 个Book结构体大小的堆内存强制转换为Book*指针 library (struct Book*)malloc(maxBooks * sizeof(struct Book)); // 判断内存分配是否失败malloc失败返回NULL if (library NULL) { printf(✗ 内存分配失败\n); exit(1); // 异常退出程序返回错误码1 } printf(✓ 初始化书架容量%d 本\n, maxBooks); } /** * destroyLibrary销毁书架、释放堆内存 * 作用程序结束前free释放动态分配的内存解决内存泄漏 * 安全判断先判断指针非空再释放防止重复free崩溃 */ void destroyLibrary() { if (library ! NULL) { free(library); // 释放堆内存 library NULL; // 置空野指针 } printf(✓ 已释放所有内存\n); } /** * ensureCapacity动态扩容核心函数 * 触发时机每次添加图书前调用 * 逻辑当前图书数量等于总容量时容量翻倍realloc重新分配更大内存 */ void ensureCapacity() { // 容量充足无需扩容直接返回 if (bookCount maxBooks) { return; } // 新容量 当前容量 * 2 int newMax maxBooks * 2; // realloc重新分配更大内存自动拷贝原有数据 struct Book* newLibrary (struct Book*)realloc(library, newMax * sizeof(struct Book)); // 扩容失败判断 if (newLibrary NULL) { printf(✗ 扩容失败内存不足\n); return; } // 更新全局指针与容量 library newLibrary; maxBooks newMax; printf(✓ 书架扩容成功当前容量%d 本\n, maxBooks); } /** * addBook新增图书适配动态内存 * 改动函数开头先调用ensureCapacity自动扩容V1无此逻辑 */ void addBook(const char* name, const char* author, const char* isbn, int stock) { ensureCapacity(); // V2新增先校验容量不足自动扩容 // 校验ISBN重复逻辑与V1完全一致 for (int i 0; i bookCount; i) { if (strcmp(library[i].isbn, isbn) 0) { printf(✗ ISBN %s 已存在\n, isbn); return; } } // 指针获取新图书位置赋值逻辑不变 struct Book* b library[bookCount]; b-id nextId; strcpy(b-name, name); strcpy(b-author, author); strcpy(b-isbn, isbn); b-stock stock; b-borrowed 0; b-borrowHistory NULL; bookCount; // 输出增加当前图书总数提示 printf(✓ 添加成功图书ID%d当前共 %d 本\n, b-id, bookCount); } /** * findBookById按ID查找图书逻辑与V1完全无改动 */ struct Book* findBookById(int id) { for (int i 0; i bookCount; i) { if (library[i].id id) { return library[i]; } } return NULL; } /** * findBookIndexById返回图书下标逻辑与V1完全无改动 */ int findBookIndexById(int id) { for (int i 0; i bookCount; i) { if (library[i].id id) { return i; } } return -1; } /** * borrowBook借书业务逻辑不变仅输出文字小幅简化 */ void borrowBook(int id) { struct Book* b findBookById(id); if (b NULL) { printf(✗ 未找到ID为 %d 的图书\n, id); return; } if (b-borrowed b-stock) { printf(✗ 借书失败《%s》库存不足\n, b-name); return; } b-borrowed; printf(✓ 借书成功《%s》已借出%d 本\n, b-name, b-borrowed); } /** * returnBook还书业务逻辑不变输出文字小幅简化 */ void returnBook(int id) { struct Book* b findBookById(id); if (b NULL) { printf(✗ 未找到ID为 %d 的图书\n, id); return; } if (b-borrowed 0) { printf(✗ 还书失败《%s》没有借出记录\n, b-name); return; } b-borrowed--; printf(✓ 还书成功《%s》已借出%d 本\n, b-name, b-borrowed); } /** * listAllBooks展示全部图书 * 改动末尾打印当前动态数组总容量maxBooksV1无容量展示 */ void listAllBooks() { if (bookCount 0) { printf(\n 书架为空\n); return; } printf(\n\n); printf(ID\t书名\t\t作者\t\t库存/可借\n); printf(\n); for (int i 0; i bookCount; i) { struct Book* b library[i]; printf(%d\t%-12s\t%-12s\t%d/%d\n, b-id, b-name, b-author, b-stock, b-stock - b-borrowed); } printf(\n); printf(总计%d 本书容量%d\n, bookCount, maxBooks); } /** * deleteBook删除图书数组移位逻辑完全复用V1无改动 */ void deleteBook(int id) { int index findBookIndexById(id); if (index -1) { printf(✗ 未找到ID为 %d 的图书\n, id); return; } if (library[index].borrowed 0) { printf(✗ 删除失败《%s》还有 %d 本借出\n, library[index].name, library[index].borrowed); return; } // 末尾元素覆盖删除位置逻辑不变 library[index] library[bookCount - 1]; bookCount--; printf(✓ 删除成功\n); } /** * showMenu菜单界面 * 改动标题改为V2动态内存版新增打印当前容量maxBooks */ void showMenu() { printf(\n\n); printf( 图书管理系统 V2指针动态内存\n); printf(\n); printf(图书总数%d 本容量%d\n, bookCount, maxBooks); printf(----------------------------------------\n); printf(1. 添加图书\n); printf(2. 借书\n); printf(3. 还书\n); printf(4. 查询图书按ID\n); printf(5. 显示所有图书\n); printf(6. 删除图书\n); printf(7. 退出程序\n); printf(----------------------------------------\n); printf(请选择操作1-7); }二、V1静态结构体数组 VS V2动态内存指针新增 / 改动内容汇总1. 头文件新增V1仅#include stdio.h、#include string.hV2新增#include stdlib.h提供malloc / realloc / free / exit动态内存函数2. 全局存储结构核心改动最大升级点V1固定大小静态数组struct Book library[MAX_BOOKS];最大 100 本栈 / 全局静态内存容量写死无法扩展V2堆内存动态指针struct Book* library NULL;移除固定宏MAX_BOOKS新增变量maxBooks记录当前动态数组分配的容量初始容量仅 10 本图书增多自动扩容3. 新增 3 个专属内存管理函数V1 完全不存在void initLibrary()程序启动时调用malloc分配初始堆内存内存分配失败直接退出程序做异常容错void ensureCapacity()核心扩容函数添加图书前自动检测容量存满时容量翻倍realloc重新分配更大内存自动迁移原有数据void destroyLibrary()程序退出前调用free释放堆内存避免长期运行产生内存泄漏释放后置空指针防止野指针4. main 函数流程改动程序开头新增initLibrary();初始化动态内存退出循环后新增destroyLibrary();释放内存V1 无内存释放操作V1 菜单选项 7 退出直接结束V2 增加内存清理步骤5. addBook 函数逻辑修改V1开头判断bookCount MAX_BOOKS固定上限V2替换为ensureCapacity();自动扩容无固定上限6. 界面输出细节改动showMenu 菜单标题更新为 V2 动态内存版本新增打印当前动态数组容量maxBookslistAllBooks 列表底部新增容量%d显示当前分配内存大小addBook 成功提示增加当前总图书数量展示7. 业务函数细微优化无逻辑变更仅文字borrowBook /returnBook 错误提示文字精简去掉多余括号信息业务判断逻辑完全不变8. 结构体、查找、删除逻辑完全复用无改动struct Book 结构体定义和 V1 一模一样findBookById /findBookIndexById 遍历查找代码无修改deleteBook 数组移位删除逻辑完全复用 V1借书、还书、单本查询核心业务逻辑无变更三、V2 版本核心优势对比 V1不再限制固定图书数量按需分配内存内存利用率更高学习堆内存、指针、malloc/realloc/free夯实 C 语言内存管理知识点手动释放内存养成无内存泄漏的规范编程习惯容量自动翻倍扩容模拟真实项目动态容器设计思路四、内容梳理在C语言中malloc、realloc 和 free 是三个用于动态内存管理的核心函数它们都定义在 stdlib.h 头文件中。 简单来说它们的作用是 malloc 申请内存realloc 调整已申请的内存大小free 释放内存。 1. malloc内存分配 作用在堆Heap上申请一块连续的、指定大小的内存空间但不会初始化其中的内容里面是垃圾值。 原型void *malloc(size_t size); 参数size 是你想要申请的字节数。 返回值成功时返回指向该内存块的指针void* 类型可强制转为任何类型失败时返回 NULL。 如何使用 c #include stdio.h #include stdlib.h int main() { int *arr; int n 5; // 申请可以存放5个int类型的数据的内存空间 // 总字节数 元素个数 * 每个元素大小 arr (int *)malloc(n * sizeof(int)); // 重要必须检查申请是否成功 if (arr NULL) { printf(内存分配失败\n); return 1; } // 使用这块内存此时里面的值是随机的需要手动赋值 for (int i 0; i n; i) { arr[i] i 1; // 初始化 printf(%d , arr[i]); } // 使用完毕后必须释放防止内存泄漏 free(arr); return 0; } 2. realloc重新分配内存 作用调整扩大或缩小之前通过 malloc 或 calloc 申请的内存块大小。它会尽量在原地扩展如果原地空间不够会找新位置并自动复制旧数据过去。 原型void *realloc(void *ptr, size_t new_size); 参数 ptr指向之前申请的内存块的指针如果传 NULL行为等同于 malloc。 new_size新的目标大小字节数。 返回值成功返回新内存的指针可能与 ptr 不同失败返回 NULL此时原内存块保持不变。 如何使用重要必须用临时指针接收返回值 c #include stdio.h #include stdlib.h int main() { int *arr; int n 3; // 1. 先申请3个int的空间 arr (int *)malloc(n * sizeof(int)); if (arr NULL) return 1; for (int i 0; i n; i) arr[i] i; // 存入 0,1,2 // 2. 现在需要扩展到能存5个int int new_n 5; int *temp; // 关键用临时指针接收realloc的返回值 temp (int *)realloc(arr, new_n * sizeof(int)); // 必须检查realloc是否成功 if (temp NULL) { printf(扩展内存失败原数据 arr 依然有效。\n); // 这里不能将 arr 置为 NULL因为它还指向原内存 // 继续使用 arr 或者 free(arr) 后退出 free(arr); return 1; } else { // 成功将 arr 指向新内存 arr temp; } // 3. 此时旧数据 (0,1,2) 被自动保留新增的空间索引3,4是未初始化的 for (int i n; i new_n; i) { arr[i] i; // 初始化新空间 } // 打印所有数据0 1 2 3 4 for (int i 0; i new_n; i) { printf(%d , arr[i]); } free(arr); return 0; } 3. free释放内存 作用将之前通过动态分配函数申请的内存归还给操作系统以便系统重新利用这些内存。 原型void free(void *ptr); 参数ptr 指向要释放的内存块的指针。 使用要点极其重要 只能释放动态分配的内存不能对栈上的变量如 int a;或静态变量调用 free。 只能释放一次释放后该指针变为“悬空指针”。再次释放Double Free会导致程序崩溃。 释放后指针置 NULL好习惯释放后原指针仍然保存着已经无效的地址容易误用。建议立即置为 NULL。 c free(arr); // 释放内存 arr NULL; // 将指针置空防止野指针 4. 总结对比与核心注意事项 函数 核心作用 是否初始化 失败时返回 注意事项 malloc 申请新内存 否垃圾值 NULL 必须检查返回值sizeof 计算大小 realloc 调整已有内存大小 新增部分不初始化 NULL 必须用临时指针接收返回值防止原指针丢失 free 释放内存 不涉及 无返回值 只能释放一次释放后指针应置 NULL 三个致命错误务必避免 内存泄漏申请了内存但没有 free程序长期运行会耗尽内存。 悬空指针free 后继续使用该指针访问已释放的内存。 重复释放对同一个指针调用两次 free。 标准使用模板 c // 申请 type *p (type *)malloc(n * sizeof(type)); if (p NULL) { /* 处理错误 */ } // ... 使用 p ... // 释放 free(p); p NULL;

相关新闻