筆者在處理程序奔潰問題的時候,遇到棧溢出的情況,棧溢出最常見的情況是:迭代調用和數組過大。數組占用占空間,所以改為了malloc方式放在堆上。想想,就想整理一下關於對多維數組的動態分配問題。
一,堆和棧的先關問題
首先,必須了解一下堆和棧的問題,可參考網上文章 ,現在稍微總結一下:
棧區(stack),由編譯器自動分配釋放,存放函數的參數值,局部變量的值等。其操作方式類似於數據結構中的棧;堆區(heap),一般由程序員分配釋放,若程序員不釋放,程序結束時可能由OS回收。注意它與數據結構中的堆是兩回事,分配方式倒是類似於鏈表;全局區(靜態區)(static),全局變量和靜態變量的存儲是放在一塊的,初始化的全局變量和靜態變量在一塊區域,未初始化的全局變量和未初始化的靜態變量在相鄰的另一塊區域,程序結束后由系統釋放;文字常量區,常量字符串就是放在這里的,程序結束后由系統釋放 ;程序代碼區,存放函數體的二進制代碼。
二,多維數組的malloc內存動態分配
對於一些需要放在堆上的數組,或者維數未知的數組,我們可以直接定義指針,在進行對其內存分配。
1,C語言動態分配二維數組
(1) 已知第二維
char (*a)[N];//指向數組的指針 a = (char (*)[N])malloc(sizeof(char) * N * m); printf("%d\n", sizeof(a));//4,指針 printf("%d\n", sizeof(a[0]));//N,一維數組 free(a);
(2) 已知第一維
char* a[M];//指針的數組 int i; for(i=0; i<M; i++) a[i] = (char *)malloc(sizeof(char) * n); printf("%d\n", sizeof(a));//4*M,指針數組 printf("%d\n", sizeof(a[0]));//4,指針 for(i=0; i<M; i++) free(a[i]);
(3) 已知第一維,一次分配內存(保證內存的連續性)
char* a[M];//指針的數組 int i; a[0] = (char *)malloc(sizeof(char) * M * n); for(i=1; i<M; i++) a[i] = a[i-1] + n; printf("%d\n", sizeof(a));//4*M,指針數組 printf("%d\n", sizeof(a[0]));//4,指針 free(a[0]);
(4) 兩維都未知
char **a; int i; a = (char **)malloc(sizeof(char *) * m);//分配指針數組 for(i=0; i<m; i++) { a[i] = (char *)malloc(sizeof(char) * n);//分配每個指針所指向的數組 } printf("%d\n", sizeof(a));//4,指針 printf("%d\n", sizeof(a[0]));//4,指針 for(i=0; i<m; i++) { free(a[i]); } free(a);
(5) 兩維都未知,一次分配內存(保證內存的連續性)
char **a; int i; a = (char **)malloc(sizeof(char *) * m);//分配指針數組 a[0] = (char *)malloc(sizeof(char) * m * n);//一次性分配所有空間 for(i=1; i<m; i++) { a[i] = a[i-1] + n; } printf("%d\n", sizeof(a));//4,指針 printf("%d\n", sizeof(a[0]));//4,指針 free(a[0]); free(a);
2,C++動態分配二維數組
(1) 已知第二維
char (*a)[N];//指向數組的指針 a = new char[m][N]; printf("%d\n", sizeof(a));//4,指針 printf("%d\n", sizeof(a[0]));//N,一維數組 delete[] a;
(2) 已知第一維
char* a[M];//指針的數組 for(int i=0; i<M; i++) a[i] = new char[n]; printf("%d\n", sizeof(a));//4*M,指針數組 printf("%d\n", sizeof(a[0]));//4,指針 for(i=0; i<M; i++) delete[] a[i];
(3) 已知第一維,一次分配內存(保證內存的連續性)
char* a[M];//指針的數組 a[0] = new char[M*n]; for(int i=1; i<M; i++) a[i] = a[i-1] + n; printf("%d\n", sizeof(a));//4*M,指針數組 printf("%d\n", sizeof(a[0]));//4,指針 delete[] a[0];
(4) 兩維都未知
char **a; a = new char* [m];//分配指針數組 for(int i=0; i<m; i++) { a[i] = new char[n];//分配每個指針所指向的數組 } printf("%d\n", sizeof(a));//4,指針 printf("%d\n", sizeof(a[0]));//4,指針 for(i=0; i<m; i++) delete[] a[i]; delete[] a;
(5) 兩維都未知,一次分配內存(保證內存的連續性)
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;//分配每個指針所指向的數組 } printf("%d\n", sizeof(a));//4,指針 printf("%d\n", sizeof(a[0]));//4,指針 delete[] a[0]; delete[] a;
多說一句:new和delete要注意配對使用,即有多少個new就有多少個delete,這樣才可以避免內存泄漏!
3,靜態二維數組作為函數參數傳遞
如果采用上述幾種方法動態分配二維數組,那么將對應的數據類型作為函數參數就可以了。這里討論靜態二維數組作為函數參數傳遞,即按照以下的調用方式:
int a[2][3];
func(a);
C語言中將靜態二維數組作為參數傳遞比較麻煩,一般需要指明第二維的長度,如果不給定第二維長度,則只能先將其作為一維指針傳遞,然后利用二維數組的線性存儲特性,在函數體內轉化為對指定元素的訪問。
首先寫好測試代碼,以驗證參數傳遞的正確性:
(1) 給定第二維長度
void func(int a[][N]) { printf("%d\n", a[1][2]); }
(2) 不給定第二維長度
void func(int* a) { printf("%d\n", a[1 * N + 2]);//計算元素位置 }
注意:使用該函數時需要將二維數組首地址強制轉換為一維指針,即func((int*)a);
三,關於數組與指針相互memcpy的問題
筆者只舉個例子:
unsigned char Buffer1[5][8]= { {1,2,3,4,5,6,7,8}, {1,2,3,4,5,6,7,8}, {1,2,3,4,5,6,7,8}, {1,2,3,4,5,6,7,8}, {1,2,3,4,5,6,7,8}, }; unsigned char *Buffer2 = NULL; int bufer_index = 0; Buffer2 = (unsigned char *)malloc(sizeof(unsigned char) * 5 * 8); memset(Buffer2, 0, sizeof(Buffer2)); for (bufer_index = 0; bufer_index < 5; bufer_index++) { memcpy(Buffer2 + (bufer_index * 8), Buffer1[bufer_index], 8 * sizeof(unsigned char)); }
memcpy回來:
for (bufer_index = 0; bufer_index < 5; bufer_index++) { memcpy(Buffer[bufer_index], Buffer2 + (bufer_index * 8), 8 * sizeof(unsigned char)); }
