C 記憶體管理示例
真實記憶體管理示例
為了演示一個實際的動態記憶體示例,我們建立了一個可以生成任意長度列表的程式。
C 語言中的常規陣列長度固定且不可更改,但使用動態記憶體,我們可以建立任意長度的列表。
示例
struct list {
int *data; // 指向儲存列表項的記憶體
int numItems; // 指示列表中當前有多少項
int size; // 指示已分配的記憶體可以容納多少項
};
void addToList(struct list *myList, int item);
int main() {
struct list myList;
int amount;
// 建立一個列表,初始容量為 10 項
myList.numItems = 0;
myList.size = 10;
myList.data = malloc(myList.size * sizeof(int));
// 檢查記憶體分配是否成功
if (myList.data == NULL) {
printf("記憶體分配失敗");
return 1; // 以錯誤程式碼退出程式
}
// 將 amount 變數指定的任意數量的項新增到列表中
amount = 44;
for (int i = 0; i < amount; i++) {
addToList(&myList, i + 1);
}
// 顯示列表內容
for (int j = 0; j < myList.numItems; j++) {
printf("%d ", myList.data[j]);
}
// 在不再需要時釋放記憶體
free(myList.data);
myList.data = NULL;
return 0;
}
// 此函式向列表中新增一項
void addToList(struct list *myList, int item) {
// 如果列表已滿,則重新分配記憶體以容納另外 10 項
if (myList->numItems == myList->size) {
myList->size += 10;
myList->data = realloc( myList->data, myList->size * sizeof(int) );
}
// 將項新增到列表末尾
myList->data[myList->numItems] = item;
myList->numItems++;
}
自己動手試一試 »
指向結構的指標:此示例有一個指向結構 myList
的指標。因為我們使用的是指向結構的指標而不是結構本身,所以我們使用箭頭語法 (->
) 來訪問結構的成員。
示例說明
此示例包含三個部分:
- 一個包含列表資料的
myList
結構 main()
函式,其中包含程式程式碼。- 一個
addToList()
函式,用於向列表中新增一項
myList
結構
myList
結構包含列表的所有資訊,包括其內容。它有三個成員:
data
- 指向包含列表內容的動態記憶體的指標numItems
- 指示列表中的項數size
- 指示已分配記憶體可以容納多少項
我們使用結構是為了方便地將所有這些資訊傳遞給函式。
main()
函式
main()
函式首先初始化列表,為其分配 10 個項的空間。
// 建立一個列表,初始容量為 10 項
myList.numItems = 0;
myList.size = 10;
myList.data = malloc(myList.size * sizeof(int));
myList.numItems
設定為 0,因為列表一開始是空的。
myList.size
用於跟蹤已保留多少記憶體。我們將其設定為 10,因為我們將為 10 個項保留足夠的記憶體。
然後我們分配記憶體,並將其指標儲存在 myList.data
中。
接著,我們包含錯誤檢查,以確定記憶體分配是否成功。
// 檢查記憶體分配是否成功
if (myList.data == NULL) {
printf("記憶體分配失敗");
return 1; // 以錯誤程式碼退出程式
}
如果一切正常,一個迴圈將使用 addToList()
函式將 44 個項新增到列表中。
// 將 amount 變數指定的任意數量的項新增到列表中
amount = 44;
for (int i = 0; i < amount; i++) {
addToList(&myList, i + 1);
}
在上面的程式碼中,&myList
是列表的指標,i + 1
是我們要新增到列表中的數字。我們選擇 i + 1
是為了讓列表從 1 開始而不是從 0 開始。您可以選擇任何數字新增到列表中。
在所有項都新增到列表後,下一個迴圈將列印列表的內容。
// 顯示列表內容
for (int j = 0; j < myList.numItems; j++) {
printf("%d ", myList.data[j]);
}
在列印完列表後,我們釋放記憶體以防止記憶體洩漏。
// 在不再需要時釋放記憶體
free(myList.data);
myList.data = NULL;
addToList()
函式
我們的 addToList()
函式將一個項新增到列表中。它接受兩個引數:
void addToList(struct list *myList, int item)
- 指向列表的指標。
- 要新增到列表的值。
該函式首先透過比較列表中項的數量和列表的大小來檢查列表是否已滿。如果列表已滿,則重新分配記憶體以容納另外 10 項。
// 如果列表已滿,則重新分配記憶體以容納另外 10 項
if (myList->numItems == myList->size) {
myList->size += 10;
myList->data = realloc( myList->data, myList->size * sizeof(int) );
}
最後,該函式將項新增到列表末尾。myList->numItems
處的索引始終位於列表末尾,因為它在每次新增新項時會增加 1。
// 將項新增到列表末尾
myList->data[myList->numItems] = item;
myList->numItems++;
我們為什麼一次預留 10 個專案?
最佳化是一個在記憶體和效能之間取得平衡的挑戰。儘管我們可能會分配一些未使用但已分配的記憶體,但過於頻繁地重新分配記憶體會效率低下。需要在分配過多記憶體和過於頻繁分配記憶體之間找到平衡。
我們為這個示例選擇了數字 10,但這取決於您預計有多少資料以及資料更改的頻率。例如,如果我們事先知道我們將有正好 44 個項,那麼我們可以分配正好 44 個項的記憶體,並且只分配一次。
完整的 stdlib 參考
有關記憶體管理函式和標準庫中其他函式的完整參考,請訪問我們的 C <stdlib.h> 庫參考。