c語言中記憶體的動態分配與釋放(多維動態陣列構建)
一. 靜態陣列與動態陣列
靜態陣列比較常見,陣列長度預先定義好,在整個程式中,一旦給定大小後就無法再改變長度,靜態陣列自己自動負責釋放佔用的記憶體。
動態陣列長度可以隨程式的需要而重新指定大小。動態陣列由記憶體分配函式(malloc)從堆(heap)上分配儲存空間,只有當程式執行了分配函式後,才為其分配記憶體,同時由程式設計師自己負責釋放分配的記憶體(free)。
二. 為什麼要使用動態陣列?
在實際的程式設計中,往往會發生這種情況,即所需的記憶體空間取決於實際輸入的資料,而無法預先確定。對於這種問題,用靜態陣列的辦法很難解決。為了解決上述問題,c語言提供了一些記憶體管理函式,這些記憶體管理函式結合指標可以按需要動態地分配記憶體空間,來構建動態陣列,也可把不再使用的空間回收待用,為有效地利用記憶體資源提供了手段。
三. 動態陣列與靜態陣列的比較
對於靜態陣列,其建立非常方便,使用完也無需釋放,要引用也簡單,但是建立後無法改變其大小是其致命弱點!
對於動態陣列,其建立麻煩,使用完必須由程式設計師自己釋放,否則嚴重會引起記憶體洩露。但其使用非常靈活,能根據程式需要動態分配大小。
四. 如何構建動態陣列?
構建動態陣列時,我們遵循下面的原則:
申請的時候從外層往裡層,逐層申請;
釋放的時候從裡層往外層,逐層釋放;
五. 構建動態陣列所需指標
對於構建一維動態陣列,需要一維指標;
對於二維,則需要一維,二維指標;
對於三維,需要一,二,三維指標;
依此類推。
六. 動態記憶體分配與釋放函式
/*動態記憶體分配與釋放函式*/
void *malloc(unsigned int size);
void *calloc(unsigned int num, unsigned int size);
void *realloc(void *p,unsigned int size);
void free(void *p);
說明:
(1)
malloc()函式成功:返回所開闢空間首地址;失敗:返回空指標;功能:向系統申請size位元組堆的空間;
calloc()成功:返回所開闢空間首地址;失敗:返回空指標;功能:按型別向系統申請num個size位元組堆的空間;
realloc()成功:返回所開闢空間首地址;失敗:返回空指標;功能:將p指向的空間變為個size位元組堆的空間;
free()沒有返回值,釋放p指向的堆空間;
(2)
規定為void *型別,這並不是說該函式呼叫後無返回值,而是返回一個結點的地址,該地址的型別為void(無型別或型別不確定),即一段儲存區的首址,其具體型別無法確定,只有使用時根據各個域值資料再確定。可以用強制轉換的方法將其轉換為別的型別。例如:
double *pd = NULL;
pd = (double *)calloc(10, sizeof(double));
表示將向系統申請10個連續的double型別的儲存空間,並用指標pd指向這個連續的空間的首地址。並且用(double)對calloc()的返回型別進行轉換,以便把double型別資料的地址賦值給指標pd。
(3)
使用sizeof的目的是用來計算一種型別的佔有的位元組數,以便適合不同的編譯器。
(4)檢查動態記憶體是否分配成功
由於動態分配不一定成功,為此要附加一段異常處理程式,不致程式執行停止,使使用者不知所措。通常採用這樣的異常處理程式段:
if (p == NULL) /* 或者if(!p)*/
{
printf("動態申請記憶體失敗!\n");
exit(1); //異常退出
}
(5)這四個函式標頭檔案均包含在<stdlib.h>中。
(6)分配的堆空間是沒有名字的,只能通過返回的指標找到它。
(7)絕不能對非動態分配儲存塊使用free。也不能對同一塊記憶體區同時用free釋放兩次,如:
free(p);
free(p);
(8)呼叫 free()時, 傳入指標指向的記憶體被釋放, 但呼叫函式的指標值可能保持不變, 因為p是作為形參而傳遞給了函式。嚴格的講, 被釋放的指標值是無效的, 因為它已不再指向所申請的記憶體區。這時對它的任何使用便可能會可帶來問題。所以在釋放一個指標指向的記憶體後,將該指標賦值為0,避免該指標成為野指標:
int *p = (int *)malloc(sizeof(int));
free(p); /*釋放p指向記憶體*/
p = 0; /*或者 p = NULL,釋放p指向的記憶體後,將p指標賦值為0,避免p指標成為野指標*/
(9)malloc與calloc的區別,對於用malloc分配的記憶體區間,如果原來沒有被使用過,則其中的每一位可能都是0;反之,如果這部分記憶體空間曾經被分配、釋放和重新分配,則其中可能遺留各種各樣的資料。也就是說,使用malloc()函式的程式開始時(記憶體空間還沒有被重新分配)能正常執行,但經過一段時間後(記憶體空間已被重新分配)可能會出現問題,因此在使用它之前必須先進行初始化(可用memset函式對其初始化為0),但呼叫calloc()函式分配到的空間在分配時就已經被初始化為0了。當你在calloc()函式和malloc()函式之間作選擇時,你需考慮是否要初始化所分配的記憶體空間,從而來選擇相應的函式。
六.動態陣列構建過程
以三維整型陣列為例int array[x][y][z]
先遵循從外到裡,逐層申請的原則:
最外層的指標就是陣列名array,他是一個三維指標,指向的是array[],array[]是二維指標,所以給array申請記憶體空間需要一個三維指標int *** p;
/*給三維陣列array[x][y][z]動態分配記憶體*/
int *** p = (int ***)malloc(x * sizeof(int **));
/*或者如下*/
array = (int ***)malloc(x * sizeof(int **))
/*指標p指向的是array三維陣列的第一維,有x個元素,所以要sizeof(x * (int **))*/
次層指標是array[],它是一個二維指標,指向的是array[][],array[][]是一維指標:
int i, j;
for (i = 0; i < x; i++)
{
array[i] = (int **)malloc(y * sizeof(int *));
}
最內層指標是array[][],它是個一維指標,所指向的是array[][][],其是個整型常量。所以給array[][]申請記憶體應:
int i, j;
for (i = 0; i < x; i++)
{
for (j = 0; j < y; j++)
{
array[i][j] = (int *)malloc(z * sizeof(int));
}
}
綜合以上三步:
/*動態構建三維陣列記憶體分配函式*/
/*
* pArr: 指向三維陣列首地址
* x: 三維陣列第一維元素個數
* y: 三維陣列第二維元素個數
* z: 三維陣列第三維元素個數
*/
void Create3DActiveArray(int ***pArr, int x, int y, int z)
{
int i, j, k;
pArr = (int ***)malloc(x * sizeof(int **));
for (i = 0; i < x; i++)
{
pArr[i] = (int **)malloc(y * sizeof(int *));
for (j = 0; j < y; j++)
{
pArr[i][j] = (int *)malloc(z * sizeof(int));
for (k = 0; k < z; k++)
{
pArr[i][j][k] = i + j + k;
}
}
}
}
記憶體釋放函式:
void Free3DActiveArray(int ***pArr, int x, int y)
{
int i, j, k;
for (i = 0; i < x; i++)
{
for (j = 0; j < y; j++)
{
free(pArr[i][j]);
pArr[i][j] = 0;
}
free(pArr[i]);
pArr[i] = 0;
}
free(pArr);
}
/*
2012年2月29日 12:00:32
目的:多維陣列構建和釋放,這裡以構建一個動態3維陣列為例
*/
#include <stdio.h>
#include <malloc.h>
void Malloc3DActiveArray(int *** pArr, int x, int y, int z);
void Free3DActiveArray(int *** pArr, int x, int y);
//void Display3DArray(int *** pArr, int x, int y, int z);
int main(void)
{
int x, y, z;
int *** array = NULL;
printf("輸入一維長度: ");
scanf("%d",&x);
printf("輸入二維長度: ");
scanf("%d",&y);
printf("輸入三維長度: ");
scanf("%d",&z);
Malloc3DActiveArray(array, x, y, z);
Free3DActiveArray(array, x, y);
array = NULL;
return 0;
}
void Malloc3DActiveArray(int *** pArr, int x, int y, int z)
{
int i, j, k;
pArr = (int ***)malloc(x * sizeof(int **));
for (i = 0; i < x; i++)
{
pArr[i] = (int **)malloc(y * sizeof(int *));
for (j = 0; j < y; j++)
{
pArr[i][j] = (int *)malloc(z * sizeof(int));
for (k = 0; k < z; k++)
{
pArr[i][j][k] = i + j + k + 1;
printf("%d ", pArr[i][j][k]);
}
printf("\n");
}
printf("\n");
}
}
void Free3DActiveArray(int *** pArr, int x, int y)
{
int i, j;
for (i = 0; i < x; i++)
{
for (j = 0; j < y; j++)
{
free(pArr[i][j]);
pArr[i][j] = 0;
}
free(pArr[i]);
pArr[i] = 0;
}
free(pArr);
}
/*
2012年2月29日 12:32:02
功能:動態構建4維陣列,學習動態構建多維陣列,並釋放多維陣列
*/
#include <stdlib.h>
#include <stdio.h>
int main()
{
int n1,n2,n3,n4;
int ****array;
int i,j,k,m;
puts("輸入一維長度:");
scanf("%d",&n1);
puts("輸入二維長度:");
scanf("%d",&n2);
puts("輸入三維長度:");
scanf("%d",&n3);
puts("輸入四維長度:");
scanf("%d",&n4);
array = (int ****)malloc(n1 * sizeof(int***));//第一維
for (i = 0; i < n1; i++)
{
array[i] = (int***)malloc(n2 * sizeof(int**)); //第二維
for (j = 0; j < n2; j++)
{
array[i][j] = (int**)malloc(n3 * sizeof(int*)); //第三維
for (k = 0; k < n3; k++)
{
array[i][j][k] = (int *)malloc(n4 * sizeof(int));//第四維
for (m = 0; m < n4; m++)
{
array[i][j][k][m] = i + j + k + m + 1;
printf("%d\t", array[i][j][k][m]);
}
printf("\n");
}
printf("\n");
}
printf("\n");
}
for (i = 0; i < n1; i++)
{
for (j = 0; j < n2; j++)
{
for (k = 0; k < n3; k++)
{
free(array[i][j][k]);//釋放第四維指標
array[i][j][k] = 0;
}
free(array[i][j]);//釋放第三維指標
array[i][j] = 0;
}
free(array[i]);//釋放第二維指標
array[i] = 0;
}
free(array);//釋放第一維指標
array = NULL;
return 0;
}
/*
在vc++6.0中輸出結果為:
-----------------------------------
輸入一維長度:
1
輸入二維長度:
2
輸入三維長度:
3
輸入四維長度:
4
1 2 3 4
2 3 4 5
3 4 5 6
2 3 4 5
3 4 5 6
4 5 6 7
Press any key to continue
*/