二維數(shù)組定義以及動態(tài)分配空間
二維數(shù)組定義以及動態(tài)分配空間
下面三種定義形式怎么理解?怎么動態(tài)分配空間?
(1)、int **Ptr;
(2)、int *Ptr[ 5 ]; 我更喜歡寫成 int* Prt[5];
(3)、int ( *Ptr )[ 5 ];
此文引自網(wǎng)上,出處不詳,但是覺得非常好。略改了一點。
多維數(shù)組一向很難,一般都采用一維數(shù)組,但是一旦要用到還真是頭疼。 閑話少說,這里我就以三個二維數(shù)組的比較來展開討論:
(1)、int **Ptr;
(2)、int *Ptr[ 5 ]; 我更喜歡寫成 int* Prt[5];
(3)、int ( *Ptr )[ 5 ];
以上三例都是整數(shù)的二維數(shù)組,都可以用形如 Ptr[ 1 ][ 1 ] 的 方式訪問其內(nèi)容;但它們的差別卻是很大的。下面我從四個方面對它們進行討論:
???? 一、內(nèi)容:
??????? 它們本身都是指針,它們的最終內(nèi)容都是整數(shù)。注意我這里說的是最終內(nèi)容,而不是中間內(nèi)容,比如你寫 Ptr[ 0 ],對于三者來說,其內(nèi)容都是一個整數(shù)指針,即 int *;Ptr[1][1] 這樣的形式才是其最終內(nèi)容。
???? 二、意義:
??????? (1)、int **Ptr 表示指向"一群"指向整數(shù)的指針的指針。
??????? (2)、int *Ptr[ 5 ] 表示指向 5 個指向整數(shù)的指針的指針,或者說Ptr有5個指向"一群"整數(shù)的指針,Ptr是這5個指針構(gòu)成的數(shù)組的地址。
??????? (3)、int ( *Ptr )[ 5 ] 表示指向"一群"指向 5 個整數(shù)數(shù)組的指針的指針。
???? 三、所占空間:
??????? (1)、int **Ptr 和 (3)、int ( *Ptr )[ 5 ] 一樣,在32位平臺里,都是4字節(jié),即一個指針。
??????? 但 (2)、int *Ptr[ 5 ] 不同,它是 5 個指針,它占5 * 4 = 20 個字節(jié)的內(nèi)存空間。
???? 四、用法:
??????? (1)、int **Ptr
??????? 因為是指針的指針,需要兩次內(nèi)存分配才能使用其最終內(nèi)容。首 先,Ptr = ( int ** )new int *[ 5 ];這樣分配好了以后,它和(2)的意義相同了;然后要分別對 5 個指針進行內(nèi)存分配,例如:
?? Ptr[ 0 ] = new int[ 20 ];?
它表示為第 0 個指針分配 20 個整數(shù),分配好以后, Ptr[ 0 ] 為指向20個整數(shù)的數(shù)組。這時可以使用下標用法 Ptr[ 0 ][ 0 ] 到Ptr[ 0 ][ 19 ] 了。
?????? 如果沒有第一次內(nèi)存分配,該 Ptr 是個"野"指針,是不能使用的,如果沒有第二次內(nèi)存分配,則 Ptr[ 0 ] 等也是個"野"指針,也是不能用的。當然,用它指向某個已經(jīng)定義的地址則是允許的,那是另外的用法(類似于"借雞生蛋"的做法),這里不作討論(下同)。
?
例子:
C語言:
//動態(tài)分配二維數(shù)組空間
{
?? m_iHight=10;//二維數(shù)組的高度
m_i;//二維數(shù)組的寬度
//動態(tài)分配一個二維數(shù)組m_ppTable內(nèi)存空間
//其類型為int
?? //m_ppTable指向該數(shù)組
?? int **m_ppTable;
?? m_ppTable=new int *[m_iHight];
//動態(tài)分配m_iHight個類型為int *的內(nèi)存空間
//分配的是行地址空間
?? for(int i=0;i
??????????? m_ppTable[i]= new int[m_iWidth];
?? //動態(tài)分配m_iWidth個類型為int的內(nèi)存空間
//分配的是某行的數(shù)值空間
}
//由此分配的二維數(shù)組空間并非是連續(xù)的
//可以使用m_ppTable[row][col]來給該二維數(shù)組賦值
//其中 0<=row
//釋放所分配的內(nèi)存空間
{
??? for(int i=0;i
?????? delete[m_iWidth]m_ppTable[i]; //以行為單位釋放數(shù)值空間
??? delete [m_iHight]m_ppTable;????? //釋放行地址空間
}
int **a;
a=(int **)calloc(sizeof(int *),n);
for (i=0;i?? a[i]=(int *)calloc(sizeof(int),n);
這樣就可以了
使用的時候就和普通的二維數(shù)組一樣
最后用
for(i=0;i?? cfree(a[i]);
cfree(a);釋放內(nèi)存
就可以了
?????? (2)、int *Ptr[ 5 ]
?????? 這樣定義的話,編譯器已經(jīng)為它分配了 5 個指針的空間,這相當于(1)中的第一次內(nèi)存分配。根據(jù)對(1)的討論可知,顯然要對其進行一次內(nèi)存分配的。否則就是"野"指針。
?????? (3)、int ( *Ptr )[ 5 ]
?????? 這種定義我覺得很費解,不是不懂,而是覺得理解起來特別吃力,也許是我不太習慣這樣的定義吧。怎么描述它呢?它的意義是"一群" 指針,每個指針都是指向一個 5 個整數(shù)的數(shù)組。
如果想分配 k 個指針,這樣寫: Ptr = ( int ( * )[ 5 ] ) new int[ 5 * k ]。
這是一次性的內(nèi)存分配。分配好以后,Ptr 指向一片連續(xù)的地址空間,
其中 Ptr[ 0 ] 指向第 0 個 5 個整數(shù)數(shù)組的首地址,Ptr[ 1 ] 指向第1個5個整數(shù)數(shù)組的首地址。
??? 綜上所述,我覺得可以這樣理解它們:
??? int ** Ptr
?
_______________________________________________________________
1. C語言動態(tài)分配二維數(shù)組
(1)已知第二維
Code-1
char (*a)[N];//指向數(shù)組的指針
a = (char (*)[N])malloc(sizeof(char *) * m);
printf("%dn", sizeof(a));//4,指針
printf("%dn", sizeof(a[0]));//N,一維數(shù)組
free(a);
(2)已知第一維
Code-2
char* a[M];//指針的數(shù)組
int i;
for(i=0; i<M; i++)
a[i] = (char *)malloc(sizeof(char) * n);
printf("%dn", sizeof(a));//4*M,指針數(shù)組
printf("%dn", sizeof(a[0]));//4,指針
for(i=0; i<M; i++)
?? free(a[i]);
(3)已知第一維,一次分配內(nèi)存(保證內(nèi)存的連續(xù)性)
Code-3
char* a[M];//指針的數(shù)組
int i;
a[0] = (char *)malloc(sizeof(char) * M * n);
for(i=1; i<M; i++)
a[i] = a[i-1] + n;
printf("%dn", sizeof(a));//4*M,指針數(shù)組
printf("%dn", sizeof(a[0]));//4,指針
free(a[0]);
(4)兩維都未知
Code-4
char **a;
int i;
a = (char **)malloc(sizeof(char *) * m);//分配指針數(shù)組
for(i=0; i<m; i++)
{
a[i] = (char *)malloc(sizeof(char) * n);//分配每個指針所指向的數(shù)組
}
printf("%dn", sizeof(a));//4,指針
printf("%dn", sizeof(a[0]));//4,指針
for(i=0; i<m; i++)
{
free(a[i]);
}
free(a);
(5)兩維都未知,一次分配內(nèi)存(保證內(nèi)存的連續(xù)性)
Code-5
char **a;
int i;
a = (char **)malloc(sizeof(char *) * m);//分配指針數(shù)組
a[0] = (char *)malloc(sizeof(char) * m * n);//一次性分配所有空間
for(i=1; i<m; i++)
{
a[i] = a[i-1] + n;
}
printf("%dn", sizeof(a));//4,指針
printf("%dn", sizeof(a[0]));//4,指針
free(a[0]);
free(a);
2.C++動態(tài)分配二維數(shù)組
(1)已知第二維
Code-6
char (*a)[N];//指向數(shù)組的指針
a = new char[m][N];
printf("%dn", sizeof(a));//4,指針
printf("%dn", sizeof(a[0]));//N,一維數(shù)組
delete[] a;
(2)已知第一維
Code-7
char* a[M];//指針的數(shù)組
for(int i=0; i<M; i++)
?? a[i] = new char[n];
printf("%dn", sizeof(a));//4*M,指針數(shù)組
printf("%dn", sizeof(a[0]));//4,指針
for(i=0; i<M; i++)
?? delete[] a[i];
(3)已知第一維,一次分配內(nèi)存(保證內(nèi)存的連續(xù)性)
Code-8
char* a[M];//指針的數(shù)組
a[0] = new char[M*n];
for(int i=1; i<M; i++)
a[i] = a[i-1] + n;
printf("%dn", sizeof(a));//4*M,指針數(shù)組
printf("%dn", sizeof(a[0]));//4,指針
delete[] a[0];
(4)兩維都未知
Code-9
char **a;
a = new char* [m];//分配指針數(shù)組
for(int i=0; i<m; i++)
{
a[i] = new char[n];//分配每個指針所指向的數(shù)組
}
printf("%dn", sizeof(a));//4,指針
printf("%dn", sizeof(a[0]));//4,指針
for(i=0; i<m; i++)
delete[] a[i];
delete[] a;
(5)兩維都未知,一次分配內(nèi)存(保證內(nèi)存的連續(xù)性)
Code-10
char **a;
a = new char* [m];
a[0] = new char[m * n];//一次性分配所有空間
for(int i=1; i<m; i++)
{
a[i] = a[i-1] + n;//分配每個指針所指向的數(shù)組
}
printf("%dn", sizeof(a));//4,指針
printf("%dn", sizeof(a[0]));//4,指針
delete[] a[0];
delete[] a;
多說一句:new和delete要注意配對使用,即有多少個new就有多少個delete,這樣才可以避免內(nèi)存泄漏!
?
3.靜態(tài)二維數(shù)組作為函數(shù)參數(shù)傳遞
如果采用上述幾種方法動態(tài)分配二維數(shù)組,那么將對應的數(shù)據(jù)類型作為函數(shù)參數(shù)就可以了。這里討論靜態(tài)二維數(shù)組作為函數(shù)參數(shù)傳遞,即按照以下的調(diào)用方式:
int a[2][3];
func(a);
C語言中將靜態(tài)二維數(shù)組作為參數(shù)傳遞比較麻煩,一般需要指明第二維的長度,如果不給定第二維長度,則只能先將其作為一維指針傳遞,然后利用二維數(shù)組的線性存儲特性,在函數(shù)體內(nèi)轉(zhuǎn)化為對指定元素的訪問。
首先寫好測試代碼,以驗證參數(shù)傳遞的正確性:
(1)給定第二維長度
Code-11
void func(int a[][N])
{
printf("%dn", a[1][2]);
}
(2)不給定第二維長度
Code-12
void func(int* a)
{
printf("%dn", a[1 * N + 2]);//計算元素位置
}
注意:使用該函數(shù)時需要將二維數(shù)組首地址強制轉(zhuǎn)換為一維指針,即func((int*)a);
1. C語言動態(tài)分配二維數(shù)組
(1)已知第二維
Code-1
char (*a)[N];//指向數(shù)組的指針
a = (char (*)[N])malloc(sizeof(char *) * m);
printf("%dn", sizeof(a));//4,指針
printf("%dn", sizeof(a[0]));//N,一維數(shù)組
free(a);
(2)已知第一維
Code-2
char* a[M];//指針的數(shù)組
int i;
for(i=0; i<M; i++)
a[i] = (char *)malloc(sizeof(char) * n);
printf("%dn", sizeof(a));//4*M,指針數(shù)組
printf("%dn", sizeof(a[0]));//4,指針
for(i=0; i<M; i++)
?? free(a[i]);
(3)已知第一維,一次分配內(nèi)存(保證內(nèi)存的連續(xù)性)
Code-3
char* a[M];//指針的數(shù)組
int i;
a[0] = (char *)malloc(sizeof(char) * M * n);
for(i=1; i<M; i++)
a[i] = a[i-1] + n;
printf("%dn", sizeof(a));//4*M,指針數(shù)組
printf("%dn", sizeof(a[0]));//4,指針
free(a[0]);
(4)兩維都未知
Code-4
char **a;
int i;
a = (char **)malloc(sizeof(char *) * m);//分配指針數(shù)組
for(i=0; i<m; i++)
{
a[i] = (char *)malloc(sizeof(char) * n);//分配每個指針所指向的數(shù)組
}
printf("%dn", sizeof(a));//4,指針
printf("%dn", sizeof(a[0]));//4,指針
for(i=0; i<m; i++)
{
free(a[i]);
}
free(a);
(5)兩維都未知,一次分配內(nèi)存(保證內(nèi)存的連續(xù)性)
Code-5
char **a;
int i;
a = (char **)malloc(sizeof(char *) * m);//分配指針數(shù)組
a[0] = (char *)malloc(sizeof(char) * m * n);//一次性分配所有空間
for(i=1; i<m; i++)
{
a[i] = a[i-1] + n;
}
printf("%dn", sizeof(a));//4,指針
printf("%dn", sizeof(a[0]));//4,指針
free(a[0]);
free(a);
2.C++動態(tài)分配二維數(shù)組
(1)已知第二維
Code-6
char (*a)[N];//指向數(shù)組的指針
a = new char[m][N];
printf("%dn", sizeof(a));//4,指針
printf("%dn", sizeof(a[0]));//N,一維數(shù)組
delete[] a;
(2)已知第一維
Code-7
char* a[M];//指針的數(shù)組
for(int i=0; i<M; i++)
?? a[i] = new char[n];
printf("%dn", sizeof(a));//4*M,指針數(shù)組
printf("%dn", sizeof(a[0]));//4,指針
for(i=0; i<M; i++)
?? delete[] a[i];
(3)已知第一維,一次分配內(nèi)存(保證內(nèi)存的連續(xù)性)
Code-8
char* a[M];//指針的數(shù)組
a[0] = new char[M*n];
for(int i=1; i<M; i++)
a[i] = a[i-1] + n;
printf("%dn", sizeof(a));//4*M,指針數(shù)組
printf("%dn", sizeof(a[0]));//4,指針
delete[] a[0];
(4)兩維都未知
Code-9
char **a;
a = new char* [m];//分配指針數(shù)組
for(int i=0; i<m; i++)
{
a[i] = new char[n];//分配每個指針所指向的數(shù)組
}
printf("%dn", sizeof(a));//4,指針
printf("%dn", sizeof(a[0]));//4,指針
for(i=0; i<m; i++)
delete[] a[i];
delete[] a;
(5)兩維都未知,一次分配內(nèi)存(保證內(nèi)存的連續(xù)性)
Code-10
char **a;
a = new char* [m];
a[0] = new char[m * n];//一次性分配所有空間
for(int i=1; i<m; i++)
{
a[i] = a[i-1] + n;//分配每個指針所指向的數(shù)組
}
printf("%dn", sizeof(a));//4,指針
printf("%dn", sizeof(a[0]));//4,指針
delete[] a[0];
delete[] a;
多說一句:new和delete要注意配對使用,即有多少個new就有多少個delete,這樣才可以避免內(nèi)存泄漏!
3.靜態(tài)二維數(shù)組作為函數(shù)參數(shù)傳遞
如果采用上述幾種方法動態(tài)分配二維數(shù)組,那么將對應的數(shù)據(jù)類型作為函數(shù)參數(shù)就可以了。這里討論靜態(tài)二維數(shù)組作為函數(shù)參數(shù)傳遞,即按照以下的調(diào)用方式:
int a[2][3];
func(a);
C語言中將靜態(tài)二維數(shù)組作為參數(shù)傳遞比較麻煩,一般需要指明第二維的長度,如果不給定第二維長度,則只能先將其作為一維指針傳遞,然后利用二維數(shù)組的線性存儲特性,在函數(shù)體內(nèi)轉(zhuǎn)化為對指定元素的訪問。
首先寫好測試代碼,以驗證參數(shù)傳遞的正確性:
(1)給定第二維長度
Code-11
void func(int a[][N])
{
printf("%dn", a[1][2]);
}
(2)不給定第二維長度
Code-12
void func(int* a)
{
printf("%dn", a[1 * N + 2]);//計算元素位置
}
注意:使用該函數(shù)時需要將二維數(shù)組首地址強制轉(zhuǎn)換為一維指針,即func((int*)a);