一般在做 int arr[M] 動態記憶體配置時長這樣

int *arr1 = (int*)malloc(sizeof(int) * M); /* for C */
int *arr2 = new int[M]; /* for C++*/

為初學者小提一下,上式是在宣告時就給初值,也可拆開來寫,等價於

int *arr1 ;
/* do something */
arr1 = (int*)malloc(sizeof(int) * M); /* for C */

int *arr2;
/* do something */
arr2 = new int[M]; /* for C++*/

以下所有說明採第一種,合起來方式。釋放時

free(arr1); // for C
delete [] arr2; // for C++

一維的時候沒什麼問題,到二維以上時便有些問題出現。

----

假設欲配置 int arr[M][N] 動態配憶體時,最簡單的做法為

/* for C */
int **arr1 = (int**)malloc(sizeof(int*)*M);
for(int i=0; i<M; ++i) arr1[i] = (int*)malloc(sizeof(int)*N); 

/* for C++ */
int **arr2 = new int*[M];
for(int i=0; i<M; ++i) arr2[i] = new int[N];

釋放時

/* for C */
for (int i=0; i<M; ++i) free(arr1[i]);
free(arr1);

/* for C++ */
for (int i=0; i<M; ++i) delete [] arr2;
delete [] arr2;

 

上面這方法可行,但有缺點

(1) 記憶體位置不連續

恕我懶得再畫記憶體空間示意圖,若要將 arr 做初始化為 0 之動作,只能這麼做

for(i=0; i<M; ++i)
    for(j=0; j<N; ++j)
        arr[i][j] = 0;

進階一點的方式頂多是 C 語言用 memset 改善,而 C++ 用 fill 方式改善。

/* for C */
for(i=0; i<M; ++i)
    memset(arr1, 0, sizeof(int) * N);

/* for C++ */
for(i=0; i<M; ++i)
    fill(arr2, arr2+N, 0);

memcpy 使用上也是有此缺點。如此操作麻煩,讀取速度慢也是一缺點。

(2) 無法以線性轉換方式操作

由於位置並非連續,故若由二維轉一維,沒辦法以線性方式做轉換。

----

改善方式其實不難,以 C 為例,配置 int arr[M][N] 時,

int **arr = (int**)malloc(sizeof(int*) * M);
int *trunk = (int*)malloc(sizeof(int) * M * N);

for(int i=0; i<M; ++i) {
    arr[i] = trunk;
    trunk += N;
}

要初始化時

memset( (void*)arr, 0, sizeof(int) * M * N);

要做指定時

arr[2][3] = 10;

要複制另一個 arr2 時

memcpy( (void*)arr, (void*)arr2, sizeof(int) * M * N);

要釋放時

free(arr[0]);
free(arr);

當然這配置方式,部份會引來爭議,原因在於裡面用到指標 + - 運算,
大多 compiler 在做 + - 運算時,是在做 offset 動作,
但標準規範裡並不保證這種事,只是大多 compiler 都這麼做。

最後附上一段 sample code。

#include <iostream>
#include <cstdlib>
using namespace std;

void display(int** arr, size_t m, size_t n)
{
    size_t i, j;
    
    cout << '\n';
    for(i=0; i<m; ++i){
        cout << '\n';
        for(j=0; j<n; ++j)
            cout << arr[i][j] << ' ';        
    }
    
}

int main()
{
    const size_t M = 10;
    const size_t N = 5;
    size_t i, j;
    int cnt;

    int*  trunk = NULL;
    int*  trunk2= NULL;
    // ---------------------------------
    // c style

    // allocate
    int** c=NULL;    
    c = (int**)malloc(sizeof(int*)*M);
    trunk = (int*)malloc(sizeof(int)*M*N);
    for(i=0; i<M; ++i){
        c[i]=trunk;
        trunk+=N;
    }
    // set value
    cnt=0;
    for(i=0; i<M; ++i)
        for(j=0; j<N; ++j)
            c[i][j]=++cnt;
    display(c, M, N);
    // release
    free(c[0]);
    free(c);

    // ---------------------------------
    // cpp style

    // allocate
    int** cpp=NULL;
    cpp = new int*[M];
    trunk2 = new int[M*N];
    for(i=0; i<M; ++i){
        cpp[i]=trunk2;
        trunk2+=N;
    }

    // set value
    cnt=0;
    for(i=0; i<M; ++i)
        for(j=0; j<N; ++j)
            cpp[i][j]=++cnt;
    display(cpp, M, N);

    // release
    delete [] cpp[0];
    delete [] cpp;
    return 0;
}


edisonx 發表在 痞客邦 PIXNET 留言(0) 人氣()