web-dev-qa-db-ja.com

Cの構造体およびポインターのmalloc

ベクトルの長さとその値を表す構造を次のように定義するとします。

struct Vector{
    double* x;
    int n;
};

ここで、ベクトルyを定義し、それにメモリを割り当てるとします。

struct Vector *y = (struct Vector*)malloc(sizeof(struct Vector));

インターネットでの検索では、xのメモリを個別に割り当てる必要があることが示されています。

y->x = (double*)malloc(10*sizeof(double));

しかし、y-> xにメモリを2回割り当てているようです。1つはyにメモリを割り当て、もう1つはy-> xにメモリを割り当てているため、メモリの無駄使いのようです。コンパイラが実際に何をしていて、yとy-> xの両方を初期化する正しい方法を教えていただければ幸いです。

前もって感謝します。

61
Pouya

いいえ、あなたはnoty->xのメモリを2回割り当てています。

代わりに、構造体(ポインターを含む)にメモリを割り当てていますplusそのポインターが指すもの。

このように考えてください:

         1          2
        +-----+    +------+
y------>|  x------>|  *x  |
        |  n  |    +------+
        +-----+

したがって、すべてを保存するには、実際に2つの割り当て(1および2)が必要です。

さらに、型はポインターであるためstruct Vector *yである必要があり、Cでmallocから戻り値をキャストしないでください。非表示にしたくない特定の問題を隠すことができます。Cはvoid*戻り値を他のポインター。

そして、もちろん、これらのベクトルの作成をカプセル化して、次のように、それらの管理を容易にすることができます。

struct Vector {
    double *data;    // no place for x and n in readable code :-)
    size_t size;
};

struct Vector *newVector (size_t sz) {
    // Try to allocate vector structure.

    struct Vector *retVal = malloc (sizeof (struct Vector));
    if (retVal == NULL)
        return NULL;

    // Try to allocate vector data, free structure if fail.

    retVal->data = malloc (sz * sizeof (double));
    if (retVal->data == NULL) {
        free (retVal);
        return NULL;
    }

    // Set size and return.

    retVal->size = sz;
    return retVal;
}

void delVector (struct Vector *vector) {
    // Can safely assume vector is NULL or fully built.

    if (vector != NULL) {
        free (vector->data);
        free (vector);
    }
}

そのような作成をカプセル化することにより、ベクターが完全に構築されるか、まったく構築されないことを保証します-それらが半分構築される可能性はありません。また、クライアントに影響を与えることなく、基礎となるデータ構造を完全に変更することもできます(たとえば、スペースと速度を犠牲にしてスパース配列にしたい場合)。

120
paxdiablo

初めて、変数Vectorにメモリを割り当てます。これは、変数xnを意味します。

ただし、xまだ有用なものを指していません

2番目の割り当ても必要である理由です。

5
Karthik T

少ないポイント

struct Vector y = (struct Vector*)malloc(sizeof(struct Vector));が間違っています

ystruct Vectorへのポインターを保持するため、struct Vector *y = (struct Vector*)malloc(sizeof(struct Vector));である必要があります。

1番目のmalloc()は、Vector構造(double + intへのポインター)を保持するのに十分なメモリのみを割り当てます

2番目のmalloc()は、実際にメモリを割り当てて10個のdoubleを保持します。

3
rajneesh

原則として、あなたはすでに正しいことをしています。必要なものには、2つのmalloc()sが必要です。

ほんのいくつかのコメント:

struct Vector y = (struct Vector*)malloc(sizeof(struct Vector));
y->x = (double*)malloc(10*sizeof(double));

あるべき

struct Vector *y = malloc(sizeof *y); /* Note the pointer */
y->x = calloc(10, sizeof *y->x);

最初の行では、Vectorオブジェクトにメモリを割り当てます。 malloc()は、割り当てられたメモリへのポインターを返すため、yはベクターポインターでなければなりません。 2行目では、10個のdoubleの配列にメモリを割り当てます。

Cでは、明示的なキャストは必要ありません。また、sizeof(struct Vector)の代わりにsizeof *yを記述する方が型の安全性に優れており、さらに入力の手間が省けます。

構造体を再配置して、次のように単一のmalloc()を実行できます。

struct Vector{    
    int n;
    double x[];
};
struct Vector *y = malloc(sizeof *y + 10 * sizeof(double));
1
Wernsey

struct Vectorにメモリを割り当てる場合、ポインタxにメモリを割り当てるだけです。つまり、アドレスを含む値が配置されるスペースにメモリを割り当てます。したがって、そのような方法では、y.xが参照するブロックにメモリを割り当てません。

0
Andremoniy

最初にmallocは、xのメモリ(doubleへのポインタ)を含む、structのメモリを割り当てます。 2番目のmallocは、xが指すdouble値にメモリを割り当てます。

0
oleg_g

実際には、Vectorと配列に同時に割り当てることにより、単一のmallocでこれを行うことができます。例えば:

struct Vector y = (struct Vector*)malloc(sizeof(struct Vector) + 10*sizeof(double));
y->x = (double*)((char*)y + sizeof(struct Vector));
y->n = 10;

これにより、Vector 'y'が割り当てられ、Vector構造体の直後(ただし、同じメモリブロック内)に、y-> xが余分に割り当てられたデータを指すようになります。

ベクトルのサイズ変更が必要な場合は、推奨されているように2つの割り当てを使用してサイズを変更する必要があります。内部のy-> x配列は、ベクトルstruct 'y'をそのままに保ちながらサイズ変更できます。

0
PQuinn