原代碼核心問題
- 結構體日期字段類型錯誤:`char riqi` 只能存 1 字符,應為 `char riqi[11]` 存「YYYY-MM-DD」。
- 輸入處理混亂:`scanf("%s", &m)` 用整數變量接收字串,導致棧破壞。
- 指針操作錯誤:`p->date.number;` 無賦值;`head` 傳遞未用指針導致鏈表頭插入失效。
- 內存洩漏/野指針:`malloc` 未檢查返回值;`free(p)` 後繼續使用 `p`。
- 緩衝區溢出風險:`gets(N)` 不安全,`scanf("%s")` 無長度限制。
- 主函數返回類型:`void main()` 不標準,應為 `int main()`。
完整修正代碼 (關鍵修正點已標註)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct store {
int number;
char name[20]; // 擴大容量
int shuliang;
int jiage;
int kucun;
int zonge;
char riqi[11]; // 修正:字串存日期
} store;
typedef struct unit {
store date;
struct unit* next;
} unit;
unit* build() {
unit* p = (unit*)malloc(sizeof(unit)); // 修正:強制轉型、檢查 NULL
if (!p) { printf("初始化失敗!\n"); return NULL; }
p->next = NULL;
p->date.number = 0; // 頭結點計數器
printf("初始化成功!\n");
return p;
}
// 傳入頭指針的指針,以便修改頭結點 (雖此處頭結點固定,但規範做法)
void sole(unit* head) {
unit *p = (unit*)malloc(sizeof(unit));
if (!p) return;
int m;
printf("=>商品編號:\n"); scanf("%d", &m); getchar();
p->date.number = m;
printf("=>賣出商品及型號:\n"); scanf("%19s", p->date.name); getchar(); // 修正:直接讀入結構體、限制長度
printf("=>請輸入數量!\n"); scanf("%d", &m); getchar(); p->date.shuliang = m;
printf("=>請輸入價格!\n"); scanf("%d", &m); getchar(); p->date.jiage = m;
printf("=>請輸入庫存!\n"); scanf("%d", &m); getchar(); p->date.kucun = m;
printf("=>請輸入日期(YYYY-MM-DD)!\n"); scanf("%10s", p->date.riqi); getchar(); // 修正
p->date.zonge = p->date.shuliang * p->date.jiage;
// 頭插法
p->next = head->next;
head->next = p;
head->date.number++;
}
void deletion(unit* head) {
unit *p = head->next, *q = head;
char N[20];
printf("=>請輸入你想刪除訂單名稱!\n"); scanf("%19s", N); getchar();
if (!p) { printf("系統無訂單可刪除!\n"); return; }
while (p) {
if (strcmp(p->date.name, N) == 0) {
q->next = p->next;
head->date.number--;
printf("=>刪除%s成功!\n", p->date.name);
free(p);
return;
}
q = p;
p = p->next;
}
printf("=>訂單不存在,刪除失敗!\n");
}
int display(unit* head) {
unit* p = head->next;
if (!p) { printf("系統無訂單!\n"); return 0; }
int m, n = 0;
while (!n) {
printf("=>1.顯示所有 2.按編號查找 3.返回\n"); scanf("%d", &m); getchar();
switch (m) {
case 1:
printf("系統擁有%d份訂單!\n名稱\t數量\t價格\t總額\t日期\n", head->date.number);
for (p = head->next; p; p = p->next)
printf("%s\t%d\t%d\t%d\t%s\n", p->date.name, p->date.shuliang, p->date.jiage, p->date.zonge, p->date.riqi);
break;
case 2:
printf("請輸入編號:"); scanf("%d", &m); getchar();
for (p = head->next; p; p = p->next)
if (p->date.number == m) {
printf("名稱\t數量\t價格\t總額\t日期\n%s\t%d\t%d\t%d\t%s\n", p->date.name, p->date.shuliang, p->date.jiage, p->date.zonge, p->date.riqi);
break;
}
if (!p) printf("訂單不存在!\n");
break;
case 3: n = 1; break;
}
}
return 1;
}
void menu() {
printf("\t1.新建系統 2.添加訂單\n\t3.刪除訂單 4.查看信息\n\t5.退出系統\n=>請選擇:");
}
int main() {
int m = 0, n, i;
unit* head = NULL;
printf("歡迎使用商品訂單管理系統\n=>是否新建? 1.是 2.否\n"); scanf("%d", &i); getchar();
if (i == 1) head = build(); else return 0;
while (!m) {
menu(); scanf("%d", &n); getchar();
switch (n) {
case 1: head = build(); break;
case 2: sole(head); break;
case 3: deletion(head); break;
case 4: display(head); break;
case 5: m = 1; break;
}
}
// 退出前釋放鏈表 (略)
return 0;
}
關鍵修正清單
- 所有 `scanf("%s")` 加寬度限制 `%19s` `%10s` 防溢出。
- 每次 `scanf` 後緊跟 `getchar()` 吸收換行符,避免下次輸入讀空。
- 結構體 `riqi` 改為字元數組,日期輸出正常。
- `malloc` 返回強制轉型 `(unit*)`,檢查 `NULL`。
- 鏈表操作統一使用 `head->next` 訪問首數據節點,頭結點 `number` 作計數器。
- 刪除邏輯修正:`q` 正確跟蹤前驅節點。
學習建議
- 先掌握「結構體定義、指針操作、鏈表基本增刪改查」三大核心,再寫業務邏輯。
- 養成「編譯零警告、運行無崩潰、輸入有邊界檢查」習慣。
- 使用調試器 (GDB/VS Debugger) 單步執行,觀察指針與內存變化,比單純看代碼高效得多。