lilika

导航

图书管理系统代码缺陷分析与二次开发实践

一,来源:
发现了b站一个同学用C语言编写的图书管理系统,代码约400行,实现了基本的增删改查、排序和文件存储功能。然而在阅读和测试过程中,发现了不少隐藏的缺陷和逻辑漏洞。原链接:[https://www.bilibili.com/read/cv12371645/?from=search&spm_id_from=333.337.0.0&opus_fallback=1]

二,运行环境
Windows 11
原来
三,主要问题 & 改进思路
1:字符串无长度限制:所有scanf("%s", ...)均未限制输入长度,极易导致缓冲区溢出。
2:整数输入无校验:若用户输入非数字,scanf失败,变量保持原值,可能导致死循环或数据错误
3:函数命名混乱:如liu(删除)、hui(修改)、hai(查找)、liuliu(排序)、ww(读文件)、you(录入),难以从名称理解功能。
4:未处理链表空情况:查找、修改、排序等函数未检查headNULL,直接遍历可能导致段错误。
5 代码风格不统一,缺少注释
改进方案:
1修改函数:找到匹配书籍后,进入子菜单修改,修改完成后通过break跳出外层循环。
2重命名函数为英文:如append_node, display_list, search_by_title, modify_by_title, delete_by_title, sort_by_year, load_from_file, save_to_file。
3所有动态内存分配后检查NULL。
4文件操作检查错误,并给出用户提示。
5在程序退出前调用DeleteMemory释放链表,避免内存泄漏。
6在 showBooks、findBook、updateBook、deleteBook、sortBooks 开头加入 if (head
NULL) { printf("链表为空!\n"); return; }

四,代码

include <stdio.h>

include <stdlib.h>

include <string.h>

define MAX_TITLE 50

define MAX_AUTHOR 30

define MAX_PUBLISHER 50

define FILENAME "books.txt"

// 图书结构
typedef struct Book {
int id;
char title[MAX_TITLE];
char author[MAX_AUTHOR];
char publisher[MAX_PUBLISHER];
int year;
float price;
struct Book* next;
} Book;

// 全局变量
Book* head = NULL;
int book_count = 0;

// ========== 工具函数 ==========
void clear_input() {
int c;
while ((c = getchar()) != '\n' && c != EOF);
}

int get_int(const char* prompt) {
int value;
printf("%s", prompt);
while (scanf("%d", &value) != 1) {
printf("输入错误,请重新输入: ");
clear_input();
}
clear_input();
return value;
}

float get_float(const char* prompt) {
float value;
printf("%s", prompt);
while (scanf("%f", &value) != 1) {
printf("输入错误,请重新输入: ");
clear_input();
}
clear_input();
return value;
}

void get_string(const char* prompt, char* buffer, int size) {
printf("%s", prompt);
fgets(buffer, size, stdin);
buffer[strcspn(buffer, "\n")] = '\0';
}

// ========== 核心功能 ==========
void add_book() {
Book* new_book = (Book*)malloc(sizeof(Book));
if (!new_book) {
printf("内存分配失败!\n");
return;
}

printf("\n=== 添加图书 ===\n");
new_book->id = (book_count == 0) ? 1001 : get_int("请输入图书ID: ");

get_string("书名: ", new_book->title, MAX_TITLE);
get_string("作者: ", new_book->author, MAX_AUTHOR);
get_string("出版社: ", new_book->publisher, MAX_PUBLISHER);
new_book->year = get_int("出版年份: ");
new_book->price = get_float("价格: ");

// 插入链表
new_book->next = head;
head = new_book;
book_count++;

printf("添加成功!\n");
}

void show_books() {
if (!head) {
printf("\n暂无图书数据!\n");
return;
}

Book* current = head;
printf("\n=== 图书列表 ===\n");
printf("ID\t书名\t\t作者\t\t出版社\t\t年份\t价格\n");
printf("------------------------------------------------------------\n");

while (current) {
printf("%d\t%-15s\t%-10s\t%-10s\t%d\t%.2f\n",
current->id, current->title, current->author,
current->publisher, current->year, current->price);
current = current->next;
}
printf("总计: %d 本\n", book_count);
}

Book* find_book(int id) {
Book* current = head;
while (current) {
if (current->id == id) return current;
current = current->next;
}
return NULL;
}

void search_book() {
if (!head) {
printf("\n暂无图书数据!\n");
return;
}

int id = get_int("\n请输入图书ID: ");
Book* book = find_book(id);

if (book) {
printf("\n找到图书:\n");
printf("ID: %d\n书名: %s\n作者: %s\n出版社: %s\n年份: %d\n价格: %.2f\n",
book->id, book->title, book->author,
book->publisher, book->year, book->price);
} else {
printf("未找到ID为 %d 的图书\n", id);
}
}

void update_book() {
if (!head) {
printf("\n暂无图书数据!\n");
return;
}

int id = get_int("\n请输入要修改的图书ID: ");
Book* book = find_book(id);

if (!book) {
printf("未找到该图书!\n");
return;
}

printf("\n=== 修改图书 (ID: %d) ===\n", id);
printf("当前信息: %s - %s\n", book->title, book->author);

get_string("新书名 (直接回车跳过): ", book->title, MAX_TITLE);
if (strlen(book->title) == 0) {
// 恢复原值
printf("书名未修改\n");
}

get_string("新作者 (直接回车跳过): ", book->author, MAX_AUTHOR);
if (strlen(book->author) == 0) {
printf("作者未修改\n");
}

get_string("新出版社 (直接回车跳过): ", book->publisher, MAX_PUBLISHER);
if (strlen(book->publisher) == 0) {
printf("出版社未修改\n");
}

char input[10];
printf("新年份 (直接回车跳过): ");
fgets(input, sizeof(input), stdin);
if (strlen(input) > 1) {
book->year = atoi(input);
} else {
printf("年份未修改\n");
}

printf("新价格 (直接回车跳过): ");
fgets(input, sizeof(input), stdin);
if (strlen(input) > 1) {
book->price = atof(input);
} else {
printf("价格未修改\n");
}

printf("修改完成!\n");
}

void delete_book() {
if (!head) {
printf("\n暂无图书数据!\n");
return;
}

int id = get_int("\n请输入要删除的图书ID: ");

Book *current = head, *prev = NULL;

while (current) {
if (current->id == id) {
printf("确定删除《%s》- %s? (y/n): ", current->title, current->author);
char confirm;
scanf(" %c", &confirm);
clear_input();

if (confirm == 'y' || confirm == 'Y') {
if (prev) {
prev->next = current->next;
} else {
head = current->next;
}
free(current);
book_count--;
printf("删除成功!\n");
} else {
printf("取消删除\n");
}
return;
}
prev = current;
current = current->next;
}

printf("未找到该图书!\n");
}

void sort_by_year() {
if (!head || !head->next) {
printf("\n无需排序!\n");
return;
}

int swapped;
Book *ptr1, *lptr = NULL;

do {
swapped = 0;
ptr1 = head;

while (ptr1->next != lptr) {
if (ptr1->year > ptr1->next->year) {
// 交换数据
int temp_id = ptr1->id;
ptr1->id = ptr1->next->id;
ptr1->next->id = temp_id;

char temp_str[MAX_TITLE];

strcpy(temp_str, ptr1->title);
strcpy(ptr1->title, ptr1->next->title);
strcpy(ptr1->next->title, temp_str);

strcpy(temp_str, ptr1->author);
strcpy(ptr1->author, ptr1->next->author);
strcpy(ptr1->next->author, temp_str);

strcpy(temp_str, ptr1->publisher);
strcpy(ptr1->publisher, ptr1->next->publisher);
strcpy(ptr1->next->publisher, temp_str);

int temp_year = ptr1->year;
ptr1->year = ptr1->next->year;
ptr1->next->year = temp_year;

float temp_price = ptr1->price;
ptr1->price = ptr1->next->price;
ptr1->next->price = temp_price;

swapped = 1;
}
ptr1 = ptr1->next;
}
lptr = ptr1;
} while (swapped);

printf("已按年份排序!\n");
show_books();
}

void save_to_file() {
FILE* file = fopen(FILENAME, "w");
if (!file) {
printf("无法保存文件!\n");
return;
}

Book* current = head;
int saved = 0;

while (current) {
fprintf(file, "%d|%s|%s|%s|%d|%.2f\n",
current->id, current->title, current->author,
current->publisher, current->year, current->price);
current = current->next;
saved++;
}

fclose(file);
printf("成功保存 %d 本图书到 %s\n", saved, FILENAME);
}

void load_from_file() {
FILE* file = fopen(FILENAME, "r");
if (!file) {
printf("无保存数据或文件损坏\n");
return;
}

// 清空现有数据
Book* current = head;
while (current) {
Book* next = current->next;
free(current);
current = next;
}
head = NULL;
book_count = 0;

char line[256];
int loaded = 0;

while (fgets(line, sizeof(line), file)) {
Book* new_book = (Book*)malloc(sizeof(Book));
if (!new_book) break;

// 解析数据
char* token = strtok(line, "|");
if (!token) continue;
new_book->id = atoi(token);

token = strtok(NULL, "|");
if (!token) continue;
strcpy(new_book->title, token);

token = strtok(NULL, "|");
if (!token) continue;
strcpy(new_book->author, token);

token = strtok(NULL, "|");
if (!token) continue;
strcpy(new_book->publisher, token);

token = strtok(NULL, "|");
if (!token) continue;
new_book->year = atoi(token);

token = strtok(NULL, "|");
if (!token) continue;
new_book->price = atof(token);

// 插入链表
new_book->next = head;
head = new_book;
loaded++;
book_count++;
}

fclose(file);
printf("成功加载 %d 本图书\n", loaded);
}

void show_stats() {
if (!head) {
printf("\n暂无数据!\n");
return;
}

Book* current = head;
float total = 0;
int oldest = 2100, newest = 1800;

while (current) {
total += current->price;
if (current->year < oldest) oldest = current->year;
if (current->year > newest) newest = current->year;
current = current->next;
}

printf("\n=== 统计信息 ===\n");
printf("图书总数: %d 本\n", book_count);
printf("总价值: %.2f 元\n", total);
printf("平均价格: %.2f 元\n", total / book_count);
printf("出版年份范围: %d - %d\n", oldest, newest);
}

void free_memory() {
Book* current = head;
while (current) {
Book* next = current->next;
free(current);
current = next;
}
}

void show_menu() {
printf("\n======== 图书管理系统 \n");
printf("1. 添加图书\n");
printf("2. 显示所有图书\n");
printf("3. 查找图书\n");
printf("4. 修改图书\n");
printf("5. 删除图书\n");
printf("6. 按年份排序\n");
printf("7. 保存数据\n");
printf("8. 加载数据\n");
printf("9. 统计信息\n");
printf("0. 退出\n");
printf("
======================\n");
printf("请选择: ");
}

int main() {
printf("欢迎使用图书管理系统!\n");

// 尝试加载已有数据
load_from_file();

int choice;
do {
show_menu();
choice = get_int("");

switch (choice) {
case 1: add_book(); break;
case 2: show_books(); break;
case 3: search_book(); break;
case 4: update_book(); break;
case 5: delete_book(); break;
case 6: sort_by_year(); break;
case 7: save_to_file(); break;
case 8: load_from_file(); break;
case 9: show_stats(); break;
case 0:
printf("正在保存数据...\n");
save_to_file();
printf("感谢使用,再见!\n");
break;
default:
printf("无效选择!\n");
}
} while (choice != 0);

free_memory();
return 0;
}
五截图改进
QQ20260310-155957
六总结
通过这次重构项目,我不仅修复了原代码的缺陷,还深入理解了软件工程的核心原则。重构过程让我认识到,优秀的软件不仅仅是功能正确,还需要具备良好的可读性、可维护性、安全性和用户体验。

posted on 2026-03-10 16:03  littlewish  阅读(19)  评论(0)    收藏  举报