首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C語言實現循環隊列:動態擴容、記憶體佈局與完整介面

     編輯:關於C語言

C語言實現循環隊列:動態擴容、記憶體佈局與完整介面

C++ 有 std::queue,C 語言需自建。本文提供生產級循環隊列實現:環狀緩衝區佈局、O(1) 入隊/出隊、動態擴容(數據搬移策略)、空滿判別、首尾元素存取,代碼結構清晰,可直接嵌入嵌入式/系統專案。

資料結構設計

typedef int QueueElem;

typedef struct {
    int front;      // 隊頭索引(指向首元素)
    int rear;       // 隊尾索引(指向末元素下一位)
    int capacity;   // 容量(元素數)
    QueueElem* elems; // 動態陣列
} Queue;

不變量:0 ≤ front, rear < capacity,size = (rear - front + capacity) % capacity,empty ⇔ front == rear,full ⇔ (rear+1)%capacity == front(犧牲一格區分空滿)。

核心操作實現

初始化與銷毀

void queueInit(Queue* q, int capacity) {
    q->front = q->rear = 0;
    q->capacity = capacity;
    q->elems = (QueueElem*)malloc(capacity * sizeof(QueueElem));
}

void queueDestroy(Queue* q) {
    free(q->elems);
    q->elems = NULL;
    q->front = q->rear = q->capacity = 0;
}

空/大小查詢

bool queueEmpty(const Queue* q) { return q->front == q->rear; }
int queueSize(const Queue* q) {
    return (q->rear - q->front + q->capacity) % q->capacity;
}

入隊(含動態擴容)

void queuePush(Queue* q, QueueElem x) {
    // 判滿並擴容
    if ((q->rear + 1) % q->capacity == q->front) {
        int newCap = q->capacity * 2;
        QueueElem* tmp = (QueueElem*)malloc(newCap * sizeof(QueueElem));
        if (!tmp) return;  // 擴容失敗策略:可改為報錯/中止
        
        if (q->front < q->rear) {
            // 數據連續:單次 memcpy
            memcpy(tmp, q->elems + q->front, queueSize(q) * sizeof(QueueElem));
            q->rear = queueSize(q);
        } else if (q->front > q->rear) {
            // 數據環繞:兩段 memcpy
            int firstSeg = q->capacity - q->front;
            memcpy(tmp, q->elems + q->front, firstSeg * sizeof(QueueElem));
            memcpy(tmp + firstSeg, q->elems, q->rear * sizeof(QueueElem));
            q->rear = queueSize(q);
        }
        q->front = 0;
        free(q->elems);
        q->elems = tmp;
        q->capacity = newCap;
    }
    
    q->elems[q->rear] = x;
    q->rear = (q->rear + 1) % q->capacity;
}

出隊與首尾存取

void queuePop(Queue* q) {
    if (queueEmpty(q)) return;
    q->front = (q->front + 1) % q->capacity;
}

QueueElem queueFront(const Queue* q) { return q->elems[q->front]; }
QueueElem queueBack(const Queue* q) {
    int idx = (q->rear - 1 + q->capacity) % q->capacity;
    return q->elems[idx];
}

記憶體佈局圖解

容量 8,front=5, rear=3 (size=6)
索引:  0   1   2   3   4   5   6   7
數據: [F] [G] [ ] [ ] [ ] [A] [B] [C] [D] [E]
          ↑               ↑
        rear=3          front=5
邏輯順序: A B C D E F G

複雜度分析

操作時間攤還時間空間
PushO(1) / O(n)擴容O(1)O(1) 額外
PopO(1)O(1)O(1)
Front/BackO(1)O(1)O(1)
Size/EmptyO(1)O(1)O(1)

關鍵設計決策解析

  1. 犧牲一格區分空滿:簡化邏輯,避免額外 size 字段或 full 標誌位
  2. 擴容時重置 front=0:數據搬移後陣列連續,後續操作無需模運算優化
  3. malloc 而非 realloc:環狀數據需兩段拷貝,realloc 無法直接支援,手動 malloc+memcpy+free 更清晰
  4. 無鎖單生產單消費:若 front/rear 僅由各自執行緒修改,且架構保證原子讀寫,可無鎖使用

泛型化擴展方向

  • 將 QueueElem 改為 void*,由調用者管理對象生命週期
  • 增加 copyFunc/freeFunc 回調,隊列擁有元素所有權
  • 提供 queueForEach 遍歷介面,配合回調處理每個元素

常見錯誤避坑

  1. 擴容後忘記更新 front/rear,導致索引錯亂
  2. queueBack 直接用 rear-1 未處理下溢(需 +capacity 再模)
  3. 未檢查 malloc 返回值,OOM 時解引用空指針
  4. 銷毀時未置空指針,導致懸空指針風險

這個循環隊列實現約 80 行核心代碼,涵蓋了環狀緩衝區的所有核心邏輯,是 C 語言工程師必備的基礎組件之一。

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