首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計疑難解答

C語言鏈式隊列出隊全為0?初始化與入隊邏輯錯誤深度解析

     編輯:程式設計疑難解答

現象描述

代碼實現了帶頭結點的鏈式隊列,入隊 0~9 後出隊 8 個元素,結果全為 0。經分析,核心問題在於 InitQueue 初始化邏輯錯誤導致頭結點丟失,進而使入隊操作無效、出隊訪問野指針。

關鍵錯誤定位

1. InitQueue 重複賦值導致頭結點丟失

Status InitQueue(LinkQueue *Q) {
    Q->front = Q->rear = (QueuePtr)malloc(sizeof(QNode)); // ① 分配頭結點
    if(!Q->front) exit(0);
    Q->front = NULL;                                      // ② 立即置空,導致內存洩漏且頭結點丟失
    return 1;
}

第 ② 行 Q->front = NULL; 直接切斷了對剛分配頭結點的引用,造成:

  • 內存洩漏:頭結點無法釋放。
  • 隊列實際為空:front 和 rear 均為 NULL(因 rear 也被賦值為 NULL)。

2. EnQueue 未處理空隊列情況

Status EnQueue(LinkQueue *Q, QElemType e) {
    QueuePtr p = (QueuePtr)malloc(sizeof(QNode));
    p->data = e; p->next = NULL;
    Q->rear->next = p;   // 此時 Q->rear 為 NULL,解引用導致崩潰或未定義行為
    Q->rear = p;
    return 1;
}

因初始化錯誤導致 Q->rear == NULL,此處 Q->rear->next 直接解引用空指針,程式理應崩潰,若未崩潰則屬於未定義行為(UB),數據實際未入隊。

3. DeQueue 邏輯假設前驅結點存在

Status Dequeue(LinkQueue *Q, QElemType *e) {
    QueuePtr p = Q->front->next;  // Q->front 為 NULL,再次解引用空指針
    *e = p->data;
    Q->front->next = p->next;
    if(Q->rear == p) Q->rear = Q->front;
    free(p);
    return 1;
}

同樣因 front == NULL 導致非法訪問。若運行環境未檢測到崩潰,讀取到的垃圾值恰好為 0,造成「出隊全為 0」的假象。

正確實現修正版

// 正確初始化:建立頭結點,front/rear 均指向它,next 置 NULL
Status InitQueue(LinkQueue *Q) {
    Q->front = Q->rear = (QueuePtr)malloc(sizeof(QNode));
    if (!Q->front) return 0; // 分配失敗返回錯誤碼,避免 exit(0)
    Q->front->next = NULL;
    return 1;
}

// 入隊:利用尾插法,rear 指向新結點
Status EnQueue(LinkQueue *Q, QElemType e) {
    QueuePtr p = (QueuePtr)malloc(sizeof(QNode));
    if (!p) return 0;
    p->data = e;
    p->next = NULL;
    Q->rear->next = p; // 關鍵:原尾結點 next 指向新結點
    Q->rear = p;       // 更新尾指針
    return 1;
}

// 出隊:刪除頭結點之後的第一個結點
Status Dequeue(LinkQueue *Q, QElemType *e) {
    if (Q->front == Q->rear) return 0; // 隊空判斷
    QueuePtr p = Q->front->next;
    *e = p->data;
    Q->front->next = p->next;
    if (Q->rear == p) Q->rear = Q->front; // 刪除最後一個元素時重置尾指針
    free(p);
    return 1;
}

調試建議

  1. 加斷點檢查指針:在 InitQueue 後立即打印 Q->front、Q->rear 地址,確認頭結點存在。
  2. 入隊後遍歷驗證:編寫輔助函數打印隊列所有節點 data,確認數據確實鏈接。
  3. 使用 Valgrind/AddressSanitizer:自動檢測內存洩漏、空指針解引用、越界訪問。

掌握「頭結點不存數據、front 指向頭、rear 指向尾、空隊列 front==rear」這一核心不變量,鏈式隊列操作即可邏輯自洽。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved