C語言 - 第二十章 | 指標 - malloc()、free()、calloc() 與 realloc()
前置作業
由於很多語法在C++
裡面,很多的語法、型態、轉型⋯等等,都非常的嚴謹,而教材又有點年紀了。
將專案修改為C
專案,請參考在 Visual Studio 2019 中建立和執行 C 專案
- 刪除.cpp檔
- 新增項目
Source.c
- 專案 -> 屬性 ->
C/C++
-> 所有選項 -> 尋找「編譯」 -> 編譯成「編譯成C程式碼(/TC)」
什麼是malloc()
、free()
、calloc()
與realloc()
?
到目前為止,都是事先宣告好所要使用的變數,當程式開始執行時,這些變數就會自動被配置記憶體空間。
然而有時有些變數並不知道何時會被使用,若希望在使用到的時候再配置空間給變數,並在變數不使用的時候,將變數所佔有的空間還給記憶體,這時候可以使用malloc()
與free()
函式。
1
| int *ptr = malloc(sizeof(int));
|
malloc()
運算子會配置一個int
需要的空間,並傳回該空間的位址,所以使用指標ptr
來儲存這個位址,這段程式只配置空間但不初始空間中的儲存值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #include <stdio.h> #include <stdlib.h>
int main() {
int *ptr = malloc(sizeof(int));
printf("空間位置:%p\n", ptr); printf("空間儲存值:%d\n", *ptr);
*ptr = 200;
printf("空間位置:%p\n", ptr); printf("空間儲存值:%d\n", *ptr);
free(ptr);
return 0; }
|
使用malloc()
函式動態配置的空間,在整個程式結束前並不會自動歸還給記憶體,必須使用free()
函式將空間還給記憶體,如上面的程式在結束前所作的動作。
在這個程式中,雖然顯示完畢後程式也就結束,但這邊還是示範了free()
的用法,而這也是個好習慣,日後程式在持續執行過程中,若大量使用malloc()
而沒有適當地使用free()
的話,由於空間一直沒有歸還,最後將導致整個記憶體空間用盡。
來看一個簡單的動態記憶體配置的應用,陣列使用的一個缺點,就是陣列的大小必須事先決定好,然而有時候無法知道要使用多大的陣列,或者希望由使用者自行決定陣列大小,這時候就可以使用動態記憶體配置,加上指標運算來解決這個問題,先說明陣列動態配置的方式。
1
| int *arr = malloc(1000 * sizeof(int));
|
這段程式碼動態配置了1000
個int
大小的空間,並傳回空間的第一個位址,配置後的空間資料是未知的,可以使用calloc()
來宣告空間配置。
1
| int *arr = calloc(1000, sizeof(int));
|
這個程式將宣告1000
個int
大小的空間,並將所有的空間值初始為0
。同樣地,使用malloc()
或calloc()
配置得來的空間,在不使用時應該使用free()
釋放。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| #include <stdio.h> #include <stdlib.h>
int main() {
int size = 0;
printf("請輸入陣列長度:"); scanf("%d", &size);
int *arr = malloc(size * sizeof(int));
printf("顯示元素值:\n"); for(int i = 0; i < size; i++) { printf("arr[%d] = %d\n", i, *(arr+i)); }
printf("指定元素值:\n"); for(int i = 0; i < size; i++) { printf("arr[%d] = ", i); scanf("%d" , arr + i); }
printf("顯示元素值:\n"); for(int i = 0; i < size; i++) { printf("arr[%d] = %d\n", i, *(arr+i)); }
free(arr);
return 0; }
|
也可以使用指標來模擬二維陣列,只要清楚二維陣列中的兩個維度的索引值之位移量就可以了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| #include <stdio.h> #include <stdlib.h>
int main() {
int m = 0; int n = 0;
printf("輸入二維陣列維度(m*n):"); scanf("%d*%d", &m, &n);
int *ptr = malloc(m * n * sizeof(int));
for(int i = 0; i < m; i++) { for(int j = 0; j < n; j++) { *(ptr + n*i + j) = i + j; } }
for(int i = 0; i < m; i++) { for(int j = 0; j < n; j++) { printf("%d\t", *(ptr+n*i+j)); } putchar('\n'); }
free(ptr);
return 0; }
|
如果要改變先前配置的記憶體大小,則可以使用realloc()
,你必須先使用malloc()
、calloc()
或realloc()
配置記憶體,而後使用所得到的位址來使用realloc()
重新配置記憶體大小。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| #include <stdio.h> #include <stdlib.h>
int main() {
int size = 0;
printf("請輸入陣列長度:"); scanf("%d", &size); int *arr1 = malloc(size * sizeof(int));
printf("指定元素值:\n"); for(int i = 0; i < size; i++) { printf("arr1[%d] = ", i); scanf("%d" , arr1 + i); }
printf("顯示元素值:\n"); for(int i = 0; i < size; i++) { printf("arr1[%d] = %d\n", i, *(arr1+i)); }
int *arr2 = realloc(arr1, sizeof(int) * size * 2); printf("顯示元素值:\n"); for(int i = 0; i < size * 2; i++) { printf("arr2[%d] = %d\n", i, *(arr2+i)); }
printf("arr1 address: %p\n", arr1); printf("arr2 address: %p\n", arr2);
free(arr2);
return 0; }
|
在這邊要注意的是,上例中,arr1
與arr2
的位址相同並不保證,realloc()
會需要複製資料來改變記憶體的大小,若原位址有足夠的空間,則使用原位址調整記憶體的大小,若空間不足,則重新尋找足夠的空間來進行配置,在這個情況下,realloc()
前舊位址的空間會被釋放掉,因此,必須使用realloc()
傳回的新位址,而不該使用舊位址,若realloc()
失敗,則傳回空指標(null
)最好是進行檢查。
1 2 3 4 5 6
| int *arr2 = realloc(arr1, sizeof(int) * size * 2); if(!arr2) { arr1 = arr2; } .... free(arr1);
|
註:以上參考了
在 Visual Studio 2019 中建立和執行 C 專案
malloc()、free()、calloc() 與 realloc()