你手頭有五個整數,丟進一個 C 數組就完事了:int numbers[5] = {10, 20, 30, 40, 50};。編譯器在棧上劃出連續 20 字節,索引從 0 到 4,一切安好。可當用戶要加第六個整數,問題就來了——棧數組沒法伸縮。它的大小在編譯時就烙進了二進制,編譯器看到 5,算出 20 字節,你的函數棧幀里就這點地兒,沒得商量。你可以預先聲明 int numbers[1000] 賭一把夠用,但“希望”從來不是內存管理策略。
變量長數組(VLA)也靠不住。雖然能寫 int numbers[n],但一旦進入函數,n 就固定了,本質還是棧上的一塊死空間。更要命的是,VLA 賴在棧上,而棧只有幾兆字節容量,塞一百萬個整數立刻炸棧,還沒有優雅的恢復手段。
![]()
真正的解法活在堆上。庫調用 malloc 在運行時向操作系統伸手要內存,要多大給多大,只要物理內存撐得住。但它只給你一把原始字節和一根指針,沒有大小追蹤,沒有邊界檢查,沒有“我現在多滿?”的簿記。拿到內存的同時,也拿到了全部責任。這里就是每個動態數組的起點:不是精巧的數據結構,而是一道最基礎的簿記題——誰來記錄存了幾個元素?誰來記錄還能存幾個?誰保證用完把內存還回去?在 C 里,答案永遠是:你自己來。
所以,第一步不是塞數據,而是建一份“元數據”。一個緊挨著數據的小結構體,專門記住這些簿記細節:
typedef struct { int *data; // 指向堆上存放元素的緩沖區的指針 size_t size; // 已存儲了多少個元素 size_t capacity;// 緩沖區最多能容納多少個元素} IntArray;
三個字段,恰是最低配的簿記。 data 指針來自 malloc(capacity * sizeof(int)) 的實際堆分配,當通過 arr->data[3] 訪問時,讀到的就是那塊分配區里的第四個整數。 size 追蹤用戶已經壓入了多少個元素,起始為 0;capacity 則是緩沖區能容納的總上限。從這里開始,一個最簡單、只容納整數、容量固定、只做“創建、壓入、銷毀”三件事的動態數組骨架就浮現了。沒有自動擴容,沒有泛型,沒有錯誤恢復,僅僅是后來一切擴展所依賴的原始骨骼。
多數 C 教程略過兩個關鍵點,而這一通手搓會讓人徹底明白:一個是為何需要這個元數據結構體——因為 malloc 不記得任何事的,一旦弄丟容量信息,蟲子就爬進來了;另一個是調用 free 的順序為何生死攸關。釋放時,必須先把 data 指向的堆內存還掉,再釋放結構體自身,否則就會留下懸掛的野指針或直接泄漏。這個順序不是書本教條,是內存管理的天然律令。
整段代碼的要害,不在于寫出多少行 C,而在于你承擔起 OS 和編譯器丟給你的那本“手工賬本”。每多壓一個元素,就得先問一句:還有空位嗎?每銷毀一次數組,就得先翻到最后一頁,把借來的緩沖區一張一張清干凈。這就是 C 語言里動態數組的真實起點——沒有黑箱,只有你自己。
特別聲明:以上內容(如有圖片或視頻亦包括在內)為自媒體平臺“網易號”用戶上傳并發布,本平臺僅提供信息存儲服務。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.