2

電磁気シミュレーションのコースワーク用のコードを書いていて、問題が発生しました。元の計算を最大10^8要素の非常に大きなメッシュに拡張することで少し余分なことをすることにしたので、今度はmalloc()を使用する必要があります。

これまでのところ、非常に優れていますが、コードをライブラリに保持してから、コンパイラのインラインオプションを使用してコンパイルすることを好むため、関数間で情報を渡す方法が必要でした。そこで、構造体を使用してメッシュのパラメーターと情報の配列へのポインターを追跡し始めました。構造体を次のように定義しました。

typedef struct {
    int    height;
    int    width;
    int    bottom; //position of the bottom node
    unsigned int***  dat_ptr;//the pointer to the array with all the data
    } array_info;

unsigned intへのトリプルポインタは、2D配列へのポインタです。そうしないと値によって渡され、関数内から変更できないため、この方法で行う必要があります。

ここで、次の関数を使用して構造体にメモリを割り当てようとすると、次のようになります。

void create_array(array_info A)//the function accepts struct of type "array_info" as argument
{
    int i;

    unsigned int** array = malloc(sizeof(*array) * A.height);//creates an array of arrays
    for(i = 0; i<A.height; ++i)
    {
        array[i] = malloc(sizeof(**array) * A.width);//creates an array for each row
    }
    *A.dat_ptr=array;//assigns the position of the array to the input pointer
}

操作を実行するとセグメンテーション違反が発生します。理由がわかりません:sizeof(* A.dat_ptr)はsizeof(array)と同じです。したがって、最悪の場合、割り当てラインではなく、ラインのどこかでジブリッシュになるはずですよね?

4

1 に答える 1

3

関数から (修正された) 構造体を返すか、array_info(通常は)array_info構造体へのポインターを関数に渡して、変更が呼び出し元の関数の値に影響するようにする必要があります。

typedef struct
{
    int    height;
    int    width;
    int    bottom;
    unsigned int **dat_ptr;  // Double pointer, not triple pointer
} array_info;

void create_array(array_info *A)
{
    unsigned int **array = malloc(sizeof(*array) * A->height);
    for (int i = 0; i < A->height; ++i)
        array[i] = malloc(sizeof(**array) * A->width);
    A->dat_ptr = array;
}

どこかでメモリ割り当てをチェックしていると思います。ただし、論理的な場所はこの関数です。途中で障害から回復するのは面倒です (ただし、プログラムを終了するのではなく、関数から戻る場合は必要です)。

void create_array(array_info *A)
{
    unsigned int **array = malloc(sizeof(*array) * A->height);
    if (array != 0)
    {
        for (int i = 0; i < A->height; ++i)
        {
             if ((array[i] = malloc(sizeof(**array) * A->width)) == 0)
             {
                 for (int j = 0; j < i; j++)
                      free(array[j]);
                 free(array);
                 array = 0;
                 break;
             }
        }
    }
    A->dat_ptr = array;
}

dat_ptrから戻ったときにメンバーが nullの場合、呼び出し元の関数は関数が失敗したことを認識しますcreate_array()。成功/失敗の戻り値を提供する方がよい場合があります。

私は C99 を使用しているので、呼び出しコードは次のようになります。

array_info array = { .height = 10, .width = 20, .dat_ptr = 0 };
create_array(&array);
if (array->dat_ptr == 0)
    ...error handling...

のコードでcreate_array()は、null ポインター、負またはゼロの幅または高さをチェックする必要がある場合があることに注意してください。要素に何を含める必要があるのか​​ はっきりしないbottomので、初期化せずに残しました。これにより、指定された初期化子を使用する言い訳が半分できました。指定されたイニシャライザを使用せずに、イニシャライザを非常に明確に記述することもできます。

于 2012-11-04T23:44:08.493 に答える