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

发布时间:2026/6/29 21:53:31
c语言项目驱动学习--实例化(图书管理)--003-代码对比 V3 完整逐行详细注释版 V2→V3 全版本新增 / 改动对比一、带完整注释源码c运行// // Created by Administrator on 2026/6/28. // /** * * 阶段V3结构体链表版 - 借阅历史管理 * * 前置版本V2 动态内存malloc/realloc书架 * 新增核心知识点单向链表、嵌套结构体、time时间戳、链表内存释放、头插法 * 新增核心功能为每本书独立存储完整借阅流水记录借阅人、借阅时间、归还时间 * 业务升级借书不再只计数生成永久历史记录查询图书可查看全部借阅日志 * */ #include stdio.h #include string.h #include stdlib.h #include time.h // V3新增时间函数头文件time()/localtime()/strftime() // // 1. 宏定义 // #define MAX_NAME 100 #define MAX_AUTHOR 50 #define MAX_ISBN 20 #define MAX_USERNAME 50 // V3新增借阅人姓名最大长度 // // V3新增结构体借阅记录单向链表节点 // struct BorrowRecord { int userId; // 借阅用户唯一ID char userName[MAX_USERNAME]; // 借阅人姓名 time_t borrowDate; // 借阅时间戳time_t长整型 time_t returnDate; // 归还时间戳值为0代表未归还 struct BorrowRecord* next; // 链表指针指向下一条借阅记录 }; // // 图书结构体V2基础上修改borrowHistory字段 // struct Book { int id; char name[MAX_NAME]; char author[MAX_AUTHOR]; char isbn[MAX_ISBN]; int stock; int borrowed; // V3改动void* 空指针替换为链表头指针struct BorrowRecord*正式启用借阅链表 struct BorrowRecord* borrowHistory; }; // // 2. 全局数据V3新增用户ID自增变量 // struct Book* library NULL; int bookCount 0; int maxBooks 10; int nextId 1001; int nextUserId 2001; // V3新增用户ID自动递增预留用户体系扩展 // // 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); // V3改动借书函数新增入参 userId、userName用于创建借阅记录 void borrowBook(int id, int userId, const char* userName); // V3改动还书新增userId参数匹配对应未归还借阅记录 void returnBook(int id, int userId); void listAllBooks(); // V3新增打印单本图书完整借阅历史链表 void showBorrowHistory(int id); void deleteBook(int id); // V3新增释放单条图书全部借阅链表内存防止内存泄漏 void freeBorrowHistory(struct BorrowRecord* head); void showMenu(); // // 4. 主程序入口 // int main() { initLibrary(); // 添加测试图书数据逻辑与V2一致 addBook(C程序设计语言, Kernighan, 978-7-111-00101-0, 5); addBook(数据结构与算法, Weiss, 978-7-111-00202-0, 3); 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) { // V3升级借书新增输入用户ID、用户名 int id, userId; char userName[MAX_USERNAME]; printf(请输入图书ID); scanf(%d, id); printf(请输入用户ID); scanf(%d, userId); printf(请输入用户姓名); scanf(%s, userName); borrowBook(id, userId, userName); } else if (choice 3) { // V3升级还书需要传入用户ID匹配借阅记录 int id, userId; printf(请输入图书ID); scanf(%d, id); printf(请输入用户ID); scanf(%d, userId); returnBook(id, userId); } else if (choice 4) { // 查询图书V3新增自动展示该图书全部借阅历史 int id; printf(请输入图书ID); scanf(%d, id); struct Book* b findBookById(id); if (b ! NULL) { printf(\n 图书信息 \n); printf(ID: %d\n书名: %s\n作者: %s\nISBN: %s\n, b-id, b-name, b-author, b-isbn); printf(库存: %d已借出: %d可借: %d\n, b-stock, b-borrowed, b-stock - b-borrowed); showBorrowHistory(id); // V3新增调用打印链表历史 } else { printf(未找到ID为 %d 的图书\n, id); } } else if (choice 5) { listAllBooks(); } else if (choice 6) { int id; printf(请输入图书ID); scanf(%d, id); deleteBook(id); } else if (choice 7) { break; } else { printf(无效选择\n); } } destroyLibrary(); printf(\n感谢使用\n); return 0; } // // 5. 基础内存函数基于V2修改新增链表释放逻辑 // /** * 初始化动态书架逻辑同V2无改动 */ void initLibrary() { library (struct Book*)malloc(maxBooks * sizeof(struct Book)); if (library NULL) { printf(内存分配失败\n); exit(1); } printf(✓ 初始化书架容量%d 本\n, maxBooks); } /** * V3重大改动销毁书架时先循环释放每本书的借阅历史链表 * V2仅释放library数组V3多层内存释放避免链表内存泄漏 */ void destroyLibrary() { // 遍历所有图书释放每本图书挂载的借阅链表 for (int i 0; i bookCount; i) { if (library[i].borrowHistory ! NULL) { freeBorrowHistory(library[i].borrowHistory); } } // 释放图书动态数组 if (library ! NULL) { free(library); library NULL; } printf(✓ 已释放所有内存\n); } /** * 动态扩容函数完全复用V2逻辑无修改 */ void ensureCapacity() { if (bookCount maxBooks) return; int newMax maxBooks * 2; 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); } /** * 添加图书新增初始化borrowHistory链表头指针为NULL */ void addBook(const char* name, const char* author, const char* isbn, int stock) { ensureCapacity(); for (int i 0; i bookCount; i) { if (strcmp(library[i].isbn, isbn) 0) { printf(✗ ISBN已存在\n); 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; // V3新增新书无借阅记录链表头置空 bookCount; printf(✓ 添加成功ID%d\n, b-id); } /** * 根据ID查找图书指针逻辑完全复用V2 */ struct Book* findBookById(int id) { for (int i 0; i bookCount; i) { if (library[i].id id) { return library[i]; } } return NULL; } /** * 根据ID查找数组下标逻辑完全复用V2 */ int findBookIndexById(int id) { for (int i 0; i bookCount; i) { if (library[i].id id) { return i; } } return -1; } // // 6. V3 全新核心链表操作相关函数 // /** * borrowBook重构借书逻辑新增链表节点创建头插法 * 1. 校验图书存在、库存充足 * 2. malloc分配BorrowRecord链表节点 * 3. 填充用户ID、姓名、当前借阅时间戳归还时间初始0 * 4. 头插法插入链表头部更新图书borrowHistory头指针 * 5. 时间格式化打印借阅详情 */ void borrowBook(int id, int userId, const char* userName) { struct Book* b findBookById(id); if (b NULL) { printf(✗ 未找到图书\n); return; } if (b-borrowed b-stock) { printf(✗ 借书失败《%s》库存不足\n, b-name); return; } // 动态分配一条借阅记录链表节点 struct BorrowRecord* record (struct BorrowRecord*)malloc(sizeof(struct BorrowRecord)); if (record NULL) { printf(✗ 内存分配失败\n); return; } // 填充节点数据 record-userId userId; strcpy(record-userName, userName); record-borrowDate time(NULL); // 获取当前系统时间戳 record-returnDate 0; // 0代表未归还状态 // 单向链表头插法新节点指向原有链表头部再更新头指针 record-next b-borrowHistory; b-borrowHistory record; b-borrowed; // time_t时间戳转可读年月日时分秒字符串 char timeStr[64]; struct tm* tm_info localtime(record-borrowDate); strftime(timeStr, sizeof(timeStr), %Y-%m-%d %H:%M:%S, tm_info); printf(✓ 借书成功\n); printf( 用户%sID:%d借阅《%s》\n, userName, userId, b-name); printf( 借阅时间%s\n, timeStr); printf( 该图书当前已借出%d 本\n, b-borrowed); } /** * returnBook遍历链表匹配用户未归还记录标记归还时间戳 * 1. 遍历借阅链表查找同userId且returnDate0的记录 * 2. 匹配成功赋值当前时间戳为归还时间借出数量-1 * 3. 格式化打印归还时间无匹配记录提示失败 */ void returnBook(int id, int userId) { struct Book* b findBookById(id); if (b NULL) { printf(✗ 未找到图书\n); return; } if (b-borrowed 0) { printf(✗ 该图书没有借出记录\n); return; } // 遍历整条借阅链表 struct BorrowRecord* p b-borrowHistory; int found 0; while (p ! NULL) { // 匹配同一用户 未归还 if (p-userId userId p-returnDate 0) { p-returnDate time(NULL); found 1; b-borrowed--; // 格式化归还时间 char timeStr[64]; struct tm* tm_info localtime(p-returnDate); strftime(timeStr, sizeof(timeStr), %Y-%m-%d %H:%M:%S, tm_info); printf(✓ 还书成功\n); printf( 用户 %sID:%d归还《%s》\n, p-userName, userId, b-name); printf( 归还时间%s\n, timeStr); break; } p p-next; } if (!found) { printf(✗ 未找到用户 %d 的借阅记录\n, userId); } } /** * showBorrowHistory遍历链表打印全部借阅流水 * 无记录则提示有记录循环读取每条节点区分已归还/未归还 */ void showBorrowHistory(int id) { struct Book* b findBookById(id); if (b NULL) { return; } if (b-borrowHistory NULL) { printf( 该图书暂无借阅记录\n); return; } printf(\n 借阅历史 \n); struct BorrowRecord* p b-borrowHistory; int count 1; while (p ! NULL) { char borrowStr[64], returnStr[64]; struct tm* tm_info; // 转换借阅时间字符串 tm_info localtime(p-borrowDate); strftime(borrowStr, sizeof(borrowStr), %Y-%m-%d %H:%M, tm_info); // 判断是否归还填充归还文本 if (p-returnDate 0) { strcpy(returnStr, 未归还); } else { tm_info localtime(p-returnDate); strftime(returnStr, sizeof(returnStr), %Y-%m-%d %H:%M, tm_info); } // 打印单条记录 printf( %d. 用户%sID:%d\n, count, p-userName, p-userId); printf( 借阅%s\n, borrowStr); printf( 归还%s\n, returnStr); printf( ---\n); p p-next; count; } printf( 共计 %d 条借阅记录\n, count - 1); } /** * freeBorrowHistory链表专用内存释放函数 * 循环逐个free链表节点防止链表内存泄漏 */ void freeBorrowHistory(struct BorrowRecord* head) { struct BorrowRecord* current head; struct BorrowRecord* next; while (current ! NULL) { next current-next; // 先保存下一个节点地址防止释放后丢失 free(current); current next; } } /** * deleteBookV3新增前置逻辑删除图书前先释放该图书的借阅链表 */ void deleteBook(int id) { int index findBookIndexById(id); if (index -1) { printf(✗ 未找到图书\n); return; } if (library[index].borrowed 0) { printf(✗ 删除失败还有 %d 本借出\n, library[index].borrowed); return; } // V3新增释放当前图书挂载的借阅历史链表 if (library[index].borrowHistory ! NULL) { freeBorrowHistory(library[index].borrowHistory); } // 数组移位删除逻辑同V2不变 library[index] library[bookCount - 1]; bookCount--; printf(✓ 删除成功\n); } /** * listAllBooksV3新增统计每本书借阅历史总条数表格新增历史列 */ 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]; // 遍历链表统计记录总数 int historyCount 0; struct BorrowRecord* p b-borrowHistory; while (p ! NULL) { historyCount; p p-next; } printf(%d\t%-12s\t%-12s\t%d/%d/%d\n, b-id, b-name, b-author, b-stock, b-stock - b-borrowed, historyCount); } printf(\n); } /** * showMenu菜单标题更新为V3功能描述标注借书记录借阅人 */ void showMenu() { printf(\n\n); printf( 图书管理系统 V3链表历史记录\n); printf(\n); printf(图书总数%d 本\n, bookCount); printf(----------------------------------------\n); printf(1. 添加图书\n); printf(2. 借书记录借阅人\n); printf(3. 还书\n); printf(4. 查询图书含历史\n); printf(5. 显示所有图书\n); printf(6. 删除图书\n); printf(7. 退出程序\n); printf(----------------------------------------\n); printf(请选择操作1-7); }二、V2 → V3 完整新增 / 改动清单分模块1. 头文件新增V2 仅有stdio.h/string.h/stdlib.hV3 新增#include time.h用途提供时间戳、时间格式化函数实现借阅 / 归还时间记录2. 宏定义新增新增#define MAX_USERNAME 50限制借阅人姓名字符串长度3. 结构体新增与修改V3 核心升级1全新结构体struct BorrowRecord单向链表节点V2 不存在存储单条借阅流水用户 ID、姓名、借阅时间、归还时间、下一条记录指针2Book 结构体字段修改V2void* borrowHistory;空预留指针 V3struct BorrowRecord* borrowHistory;链表头指针正式绑定借阅记录链表4. 全局变量新增新增int nextUserId 2001;预留用户自增 ID为后续独立用户模块做扩展5. 函数声明大量新增 原有函数入参修改新增 4 个链表专属函数void showBorrowHistory(int id);打印图书借阅历史void freeBorrowHistory(struct BorrowRecord* head);释放整条链表内存原有业务函数签名修改V2borrowBook(int id)→ V3borrowBook(int id, int userId, const char* userName)V2returnBook(int id)→ V3returnBook(int id, int userId)保留不变函数initLibrary / ensureCapacity / addBook / findBookById / findBookIndexById / deleteBook / listAllBooks / showMenu6. main 主程序逻辑改动菜单 2 借书增加输入用户 ID、用户姓名菜单 3 还书增加输入用户 ID菜单 4 查询图书查询完成自动调用showBorrowHistory()展示历史记录菜单文字描述更新标注 “记录借阅人”“含历史”7. 基础内存函数改动destroyLibrary () 完全重构V2仅 free 图书动态数组V3先循环遍历所有图书调用freeBorrowHistory()释放每本书的借阅链表再释放图书数组解决多层内存泄漏addBook () 小幅修改新增b-borrowHistory NULL;初始化链表头指针新书无借阅记录8. 全新实现链表业务函数V2 无对应逻辑borrowBook头插法创建链表节点记录时间戳格式化打印借阅信息returnBook遍历链表匹配用户未归还记录写入归还时间showBorrowHistory循环遍历链表格式化输出每一条借阅流水区分未归还状态freeBorrowHistory标准单向链表释放逻辑逐个释放节点9. deleteBook () 新增前置逻辑删除图书前判断是否存在借阅链表存在则先释放链表内存再执行图书删除10. listAllBooks () 表格升级表头新增「历史」列展示该图书总借阅记录条数内部增加链表循环统计 historyCount11. showMenu () 界面更新系统标题改为 V3链表 历史记录功能选项增加备注借书记录借阅人、查询图书含历史三、V3 对比 V2 核心新增能力总结单向链表实战掌握结构体嵌套链表、头插法、链表遍历、链表内存释放时间处理time_t 时间戳、本地时间转换、自定义时间格式化输出多层堆内存管理图书数组 每本书独立链表双层动态内存完整释放无泄漏完整业务日志永久保存每一次借阅、归还记录支持追溯历史用户体系雏形引入借阅人 ID、姓名字段可扩展独立用户管理模块数据状态区分通过 returnDate0 标记未归还图书精准匹配对应用户借阅记录