2

OS X 10.6で実行されているCコードをいくつか作成しましたが、これはたまたま遅いので、valgrindを使用してメモリリークなどをチェックしています。これを実行しているときに気付いたことが1つあります。

このように2D配列にメモリを割り当てると、次のようになります。

double** matrix = NULL;
allocate2D(matrix, 2, 2);

void allocate2D(double** matrix, int nrows, int ncols) {
    matrix = (double**)malloc(nrows*sizeof(double*));
    int i;
    for(i=0;i<nrows;i++) {
        matrix[i] = (double*)malloc(ncols*sizeof(double));
    }
}

次に、行列のメモリアドレスが0x0であることを確認します。

しかし、私がそうするなら

double** matrix = allocate2D(2,2);

double** allocate2D(int nrows, int ncols) {
    double** matrix = (double**)malloc(nrows*sizeof(double*));
    int i;
    for(i=0;i<nrows;i++) {
        matrix[i] = (double*)malloc(ncols*sizeof(double));
    }
return matrix;
}

これは正常に機能します。つまり、新しく作成されたメモリへのポインタが返されます。

メモリを解放するためのfree2D関数もある場合。正しく解放されていないようです。つまり、ポインタは、0x0(デフォルトの可能性があると思いました)ではなく、freeを呼び出す前と同じアドレスを指します。

void free2D(double** matrix, int nrows) {
    int i;
    for(i=0;i<nrows;i++) {
        free(matrix[i]);
    }
free(matrix);
}

私の質問は次のとおりです。malloc/freeがどのように機能するかを誤解していますか?そうでなければ、誰かが何が起こっているのかを提案できますか?

アレックス

4

3 に答える 3

4

ポインタを解放しても、ポインタの値は変更されません。nullにする場合は、明示的に0に設定する必要があります。

于 2010-10-16T10:05:45.830 に答える
3

最初の例では、によって返されたポインターのみmallocをローカル変数に格納しました。関数が戻ると失われます。

C言語での通常の方法は、関数の戻り値を使用して、割り当てられたオブジェクトへのポインターを呼び出し元に戻すことです。Armenが指摘したように、関数が出力を格納する場所へのポインターを渡すこともできます。

void Allocate2D(double*** pMatrix...)
{
   *pMatrix = malloc(...)
}

しかし、ほとんどの人は見るとすぐに悲鳴を上げると思います***

また、ポインタの配列は行列の効率的な実装ではないと考えるかもしれません。各行を個別に割り当てると、メモリの断片化、mallocオーバーヘッド(各割り当てには、格納する必要のある追加のポインタは言うまでもなく、いくつかの簿記が含まれるため)、およびキャッシュミスが発生します。また、マトリックスの要素への各アクセスには、1つだけではなく、2つのポインター逆参照が含まれるため、ストールが発生する可能性があります。最後に、マトリックスの割り当てを行うには、さらに多くの作業が必要です。それぞれの失敗をチェックしmalloc、いずれかが失敗した場合は、すでに実行したすべてをクリーンアップする必要があるためです。

より良いアプローチは、1次元配列を使用することです。

double *matrix;
matrix = malloc(nrows*ncols*sizeof *matrix);

次に、要素(i、j)にとしてアクセスしますmatrix[i*ncols+j]。潜在的な欠点は、乗算(古いCPUでは遅いが、現代のCPUでは速い)と構文です。

さらに良いアプローチは、過度の一般性を求めないことです。SOのほとんどの行列コードは、任意の行列サイズが必要になる可能性のある高度な数値数学用ではなく、2x2、3x3、および4x4が実際に使用される唯一の行列サイズである3Dゲーム用です。その場合は、次のようなものを試してください

double (*matrix)[4] = malloc(4*sizeof *matrix);

matrix[i][j]次に、単一の間接参照と非常に高速な定数による乗算の場合と同様に、要素(i、j)にアクセスできます。また、matrixローカルスコープまたは構造内でのみ必要な場合は、次のように宣言します。

double matrix[4][4];

C型システムと上記の宣言にあまり精通していない場合は、とにかくすべての行列を構造体でラップするのが最善かもしれません。

struct matrix4x4 {
    double x[4][4];
};

そうすれば、宣言、ポインタキャスト、割り当てなどがより身近になります。matrix.x[i][j]唯一の欠点は、の代わりにまたは(構造体へのポインタの構造体であるmatrix->x[i][j]かどうかに応じて)のようなことを行う必要があることです。matrixmatrix[i][j]

編集:行列を行ポインターの配列として実装する1つの便利なプロパティを考えました。これにより、行の順列が簡単な操作になります。アルゴリズムで多くの行の順列を実行する必要がある場合、これは有益な場合があります。ただし、小さな行列の場合はそれほどメリットがなく、列の順列をこのように最適化することはできません。

于 2010-10-16T18:10:46.943 に答える
1

C ++では、参照によってポインタを渡す必要があります:)

Allocate2D(double**& matrix...)

何が起こっているかについて-あなたはNULLのポインタを持っています、あなたはそのポインタのコピーをmamoryを割り当てた関数に渡し、新しく割り当てられたメモリのアドレスでポインタのコピーを初期化しますが、元のポインタはNULLのままです。無料の場合は、ポインタの値のみが関係するため、参照を渡す必要はありません。HTH

Cには参照がないため、ポインタを渡すことができます。

Allocate2D(double*** pMatrix...)
{
   *pMatrix = malloc(...)
}

後で次のように呼び出します

Allocate2D(&matrix ...)
于 2010-10-16T10:00:22.160 に答える