第 13 章 動態配置記憶體 程式設計與生活 - 使用 C 語言 Shi-Huang Chen Spring 2013 第 13 章 動態配置記憶體 13-1 記憶體配置函式 malloc( ) 13-2 動態配置結構陣列
配置記憶體 預估需求數量的範圍是一項不容易的學問 例 : 大到預估今年國家預算, 小到預估櫥窗裡展示的毛線衣, 需要多少磅毛線才能織成 撰寫程式時, 一樣無法預估程式執行所需的記憶體空間 例 : 輸入 n 個資料並儲存 如果宣告一個有 100 個元素的一維陣列來儲存 n 個資料, 會發生什麼現象呢? 當 n<100 時, 會閒置一些沒有使用到的記憶體空間 ; 當 n>100 時, 則預留的記憶體空間又不夠
動態配置記憶體模式 隨著問題範圍 ( 或大小 ) 的不同, 動態向系統要剛剛好的記憶體空間, 並在其不再需要時或程式結束前, 將其回收歸還系統, 使記憶體的空間充分被利用 13-1 記憶體配置函式 malloc( ) 函式名稱函式原型功能傳回原型宣告所在的標頭檔 malloc( ) void *malloc(size_t size); 說明 :malloc( 無號數整數變數或常數 size) 動態配置記憶體空間所配置記憶體空間的起始位址, 但目前此起始位址尚為未指定指向何種資料型態 stdlib.h
說明 malloc( ) 函式被呼叫時, 需傳入參數 (size), 它的資料型態為 size_t, 表示必須使用無號數整數變數或常數 malloc( ) 函式被呼叫時, 會配置 size 位元組的記憶體空間, 並傳回這個無資料型態的記憶體空間之起始位址 說明 ( 續 ) 由於不同資料型態的資料, 所佔記憶體空間大小也有所不同, 因此,size 可以設定成 sizeof( 資料型態 )* 幾個 例 : 動態配置兩個整數資料的記憶體空間, 則 size 設定為 2*sizeof(int) sizeof( 資料型態 ), 表示某資料型態所佔的記憶體空間
13-1-1 動態配置一維陣列 動態配置有 n 個元素的一維陣列之語法如下 : 資料型態 * 指標名稱 =( 資料型態 *) malloc(n * sizeof( 資料型態 )); 說明 因 malloc( ) 函式被呼叫時, 會傳回無資料型態的記憶體空間之起始位址, 所以必須宣告一個指標名稱來接收 語法中的每一個資料型態都必須相同 ( 資料型態 *) 表示將 malloc( ) 函式所配置記憶體空間的起始位址, 強制轉型為某資料型態的指標 malloc( ) 函式被呼叫時, 所配置空間並不會自動設定初值
例 : 動態配置有兩個元素的一維整數陣列 int *ptr = (int *) malloc(2 * sizeof(int)); [ 說明 ] 因 sizeof(int)=4, 所以動態配置了 2*4=8 個位元組 (int *) 表示將 malloc( ) 函式所配置記憶體空間的起始位址強制轉型為整數型態的指標 動態宣告的 ptr 雖然是整數指標變數, 也可以當一維整數陣列變數, 此時陣列元素為 ptr [0] 及 ptr[1] 範例 1: 寫一程式, 動態配置有兩個元素的一維整數陣列, 並將其元素的值設成 1 及 2 #include <stdio.h> #include <stdlib.h> int main() int *ptr = (int *) malloc(2 * sizeof(int)); *ptr=1; *(ptr+1)=2; printf("*ptr =%d\n", *ptr); printf("*(ptr+1)=%d\n", *(ptr+1)); free(ptr); // 回收動態配置的 ptr 陣列的記憶體 system("pause"); return 0; }
程式解說 (1) 第 6 列 ~ 第 10 列, 可以改成 ptr[0]=1; ptr[1]=2; printf("ptr[0]=%d\n", ptr[0]); printf("ptr[1]=%d\n", ptr[1]); 執行結果 ptr[0]=1 ptr[1]=2 (2) 回收一維動態陣列的語法如下 : free( 指標名稱 ); // 回收動態配置的記憶體 執行結果 *ptr=1 *(ptr+1)=2
13-1-2 動態配置二維陣列 動態配置二維陣列時, 必須從第一維陣列開始配置 第一維陣列所配置的記憶體, 主要是記錄 第二維陣列的起始位址 而第二維陣列才是儲存資料的記憶體位址 動態配置有 m*n 個元素的二維陣列之步驟如下 : 步驟 1. 配置第一維陣列, 其內容為一維指標陣列資料型態 ** 指標名稱 = ( 資料型態 **) malloc(m*sizeof( 資料型態 *)); 步驟 2. 配置第二維陣列, 其內容為一維陣列 for (i=0;i<m;i++) 指標名稱 [i]= ( 資料型態 *) malloc(n*sizeof( 資料型態 ));
說明 m 為第一維陣列的元素個數,n 為第二維陣列的元素個數 步驟中的每一個資料型態都必須相同 ( 資料型態 **) 表示 malloc( ) 函式所配置記憶體空間的起始位址, 強制轉型為某資料型態的指標, 並指定給第一維陣列 ( 資料型態 *) 表示 malloc( ) 函式所配置記憶體空間的起始位址, 強制轉型為某資料型態的指標, 並指定給第二維陣列 例 : 動態配置有 2*3 元素的二維單精確浮點數陣列 int i; float *ptr = (float **) malloc(2 * sizeof(float *)); for (i=0;i<3;i++) ptr[i]=(float *) malloc(3*sizeof(float));
說明 第一維陣列的元素有 2 個, 第二維陣列的元素有 3 個, 且 sizeof(float)=4, 所以動態配置了 2*3*4=24 個位元組 動態宣告的 ptr 雖然是單精確浮點數指標變數, 也可以當二維單精確浮點數陣列變數, 此時陣列元素為 ptr[0][0] ptr[0][1] ptr[0][2] ptr[1][0] ptr[1][1] 及 ptr[1][2] 範例 3: 寫一程式, 動態配置有 2*3=6 個元素的二維單精確符點數陣列, 並將其元素的值設成 1,2,3,4,5,6 #include <stdio.h> #include <stdlib.h> int main() int i,j; float k=1; float **ptr = (float **) malloc(2 * sizeof(float *)); for (i=0;i<3;i++) ptr[i]=(float *) malloc(3*sizeof(float)); for (i=0;i<6;i++) *(*ptr+i)=k; printf("*(*ptr+%d)=%.0f\n",i,*(*ptr+i)); k++; }
// 回收 ptr 陣列的第二維記憶體 for (i=0;i<2;i++) free(ptr[i]); free(ptr); // 回收 ptr 陣列的第一維記憶體 system("pause"); return 0; } 執行結果 *(*ptr+0)=1 *(*ptr+1)=2 *(*ptr+2)=3 *(*ptr+3)=4 *(*ptr+4)=5 *(*ptr+5)=6 程式解說 (1) 第 13 列中的 *ptr+i 代表從第二維陣列的起始記憶體位址, 移動 i 個 float 所佔的記憶體位址, 且 *ptr 表示第二維陣列儲存資料的記憶體位址 不可寫成 ptr+i, 因為其代表第一維陣列儲存資料的記憶體位址, 且一次移動 3*i 個 float 所佔的記憶體位址
(2) 第 11 列 ~ 第 17 列, 可以改成 for (i=0;i<2;i++) for (j=0;j<3;j++) ptr[i][j]=k; printf("ptr[%d][%d]=%.0f\n",i,j,ptr[i][j]); k++; } 執行結果 ptr[0][0]=1 ptr[0][1]=2 ptr[0][2]=3 ptr[1][0]=4 ptr[1][1]=5 ptr[1][2]=6
(3) 回收動態配置的二維陣列之記憶體時, 要特別注意回收的順序 回收時的順序剛好與配置時的順序相反, 即從高維陣列的記憶體往第一維陣列的記憶體 若從低維陣列的記憶體往高維陣列的記憶體, 這樣將失去指向高維的指標, 即不能再使用高維的指標 回收有 m*n 個元素的二維動態陣列之步驟如下 : 步驟 1. 回收動態配置的 ptr 陣列的第二維記憶體 for (i=0;i<m;i++) free( 指標名稱 [i]); 步驟 2. 回收動態配置的第一維陣列的記憶體 free( 指標名稱 );
13-2 動態配置結構陣列 動態配置有 n 個元素的一維結構陣列之語法 : struct 結構名稱 * 指標名稱 =(struct 結構名稱 *) malloc(n * sizeof(struct 結構名稱 )); 說明 (1) 因 malloc( ) 函式被呼叫時, 會傳回無資料型態的記憶體空間之起始位址, 所以必須宣告一個指標名稱來接收 (2) 語法中的每一個結構名稱都必須相同 (3) (struct 結構名稱 *) 表示將 malloc( ) 函式所配置記憶體空間的起始位址, 強制轉型為 struct 結構名稱的指標 (4) malloc( ) 函式被呼叫時, 所配置空間並不會自動設定初值
例 : 假設結構定義如下 struct student char name[9]; int age; char tel[11]; } 動態配置有 2 個元素的一維結構陣列 解 : struct student *ptr = (struct student *) malloc (2 * sizeof(struct student)); 說明 (1) 因 int 所佔的記憶體空間為 struct student 的成員變數中最大的, 且 sizeof(int)=4, 所以 sizeof(strut student)=ceil((float)9/4)*4+ ceil((float)4/4) *4+ceil((float)11/4)*4=28(12+4+12), 因此共動態配置了 2*28=56 個位元組 ( 參考 12-1-1 定義結構資料型態 ) (2) (strut student *) 表示將 malloc( ) 函式所配置記憶體空間的起始位址強制轉型為結構型態的指標 (3) 動態宣告的 ptr 雖然是結構指標變數, 也可以當一維結構陣列變數, 此時結構陣列元素為 ptr[0] 及 ptr[1]
範例 7: 寫一程式, 動態配置有兩個元素的一維結構陣列, 且結構定義如下 : struct student char name[9]; int age; char tel[11]; } 輸入一維結構陣列的成員資料並印出 #include <stdio.h> #include <stdlib.h> int main() int i; struct student char name[9]; int age; char tel[11]; }; struct student *ptr = (struct student *) malloc(2 * sizeof(struct student));
for (i=0;i<2;i++) printf(" 輸入第 %d 個學生的名字 :",i+1); scanf("%s",ptr->name); printf(" 年齡 :"); scanf("%d",&ptr->age); printf(" 電話 :"); scanf("%s",ptr->tel); printf(" 第 %d 個學生的名字 :",i+1); printf("%s ",ptr->name); printf(" 年齡 :"); printf("%d ",ptr->age); printf(" 電話 :"); printf("%s\n",ptr->tel); } system("pause"); return 0; } 執行結果 輸入第 1 個學生的名字 : 一郎年齡 :19 電話 :06-651 第 1 個學生的名字 : 一郎年齡 :19 電話 : 06-651 輸入第 2 個學生的名字 : 松板年齡 :18 電話 :04-239 第 2 個學生的名字 : 松板年齡 :18 電話 : 04-239