轉自:http://tsindahui.blog.sohu.com/84512010.html
在C中動態分配內存的,對於單個變量,字符串,一維數組等,都是很容易的。C中動態分配二維數組的方法,很少有C語言書中描述,我查找了有的C語言書中提到了一個方法:
假定二維數組的維數爲[M][N]
分配是可以這樣:
int **ptr=new int*[M]; //////這是先動態分配一個包含有M個指針的數組,即指先分配一個針數組
///////////指針數組的首地址保存在ptr中
for(int i=0;i<M;i++)
ptr[i]=new int[N]; ////////////爲指針數組的每個元素賦一個地址,
////這個地址是指向一維數組的地址,也即是爲針元數組的每個元素分配一個數組
一個源代碼的例子爲:
int **pMatrix = new int*[row];
for(int i = 0; i < row; i++)
{
pMatrix[i] = new int[column];
for(int j = 0; j < column; j++)
{
pMatrix[i][j] = (i+j); ///////簡單的初始化
}
}
這樣創建一個數組有個嚴重的問題,就是它的內存不連續,行與行之間的內存不連續,雖然可以用[i][j]下標訪問,無法滿足用指向二維數組元素型別的指針變量來訪問整個數組的要求
例如不能如下訪問每個二維數組元素:
int * p = NULL;
for(p = pMatrix[0]; p < pMatrix[0]+column * row; p++)
{
int fff = *(pme);
}
而這種訪問方式對於真正的二維數組是完全可以的。出現這種原因就是因爲行與行之間的內存不連續造成的。
所以,這中方式創建的動態二維數組,不是真正意義上的二維數組。
那麼什麼是真正的二維數組呢?C語言中的二維數組在內存組織形式是按行存儲的連續的內存區域。所以,必須保證數組元素是按行存儲的,而且也是最重要的是內存要連續。
所以,我寫出瞭如下的一個方法:
假定二維數組的元素變量類型是MyType;可以是C語言接受的除void之外的任何類型,因爲編譯器不曉得void類型的大小;例如int,float,double等等類型;
int row = 2; /////暫假定行數是2,這個可以在運行時刻決定;
int column = 3;/////暫假定列數是2,這個可以在運行時刻決定;
void **ptdhead = NULL; //////////在後面說明爲什麼要用void**類型
void **ptdBody = NULL;//////////在後面說明爲什麼要用void**類型
ptdhead = (void **)malloc(sizeof(void*)*row + sizeof(MyType)*row*column);
if(!ptdhead)
return FALSE;
ptdBody = ptdhead + row ;
for(int ncount = 0; ncount < row; ncount++)
ptdhead[ncount] = ptdBody + ncount * column* sizeof(MyType)/sizeof(void*);
MyType**ptdheadRealse;
ptdheadRealse = (MyType**)ptdhead;///////////////////強制轉換爲自己程序需要的二維數組元素類型的指針
ptdhead = NULL;
for(int i = 0; i < row; i++ )
{
for(int j = 0; j< column; j++)
{
ptdheadRealse[i][j] = i+j; ////////進行簡單的初始化;
}
}
這樣的一種方法動態分配的二維數組,內存是連續的,是真正意義的C語言二維數組,滿足所有二維數組訪問的方法,而且內存利用效率高,程序性能好。
這樣一種分配方法要理解的是一下一點概念:
體會,只要是指針都可以帶[],不管使直接指針,還是間接指針,都可以用下標,只要使指針就可以了,這個很關鍵;
另外就是要明白void*的指針是不能夠用於加減法的,因爲系統不曉得一個void型的大小,
但是void**指針卻是可以進行加減法,進行指針偏移的,因爲void*型大小使知道的,
所以,編譯器使可以計算出偏移地址的。
由於void型,系統不曉得大小,所以,void *p = (void*)malloc(3); 編譯器無法通過如 void *q = p+3;
我們知道假設一個整型變量nCont在32位機器上是4個字節,q是指向nCont的指針變量,q的值,也就是nCont的地址是0x00032ec0,那麼q+1的值爲0x0x00032ec0+1*4,這是C語言中計算指針表達式值的方法。即q+1的值爲q+1*sizeof(int);
從這裏,我們可以理解爲什麼我們用void**作爲動態分配內存函數返回的類型,因爲,如果返回的是void*類型,我們無法計算地址的偏移量,即無法計算出數組首元素的地址,也就是數組的地址。當然,我們可以不用void**,可以用除了void*的任何C中內嵌的簡單類型,不過如果考慮使用起來簡單,方便,那麼我覺得還是懸着用void**,或者char*;選擇char*類型方便的是,char類型的大小是1,那麼元素的個數,即等於地址的偏移量。
以上是我對C語言動態分配二維數組的一個見解。歡迎批評指正。