web-dev-qa-db-ja.com

Cのメモリ割り当てを理解するのに少し問題がある

だから私はCでプログラミングする方法を学び、動的メモリ割り当てについて学び始めています。私が知っていることは、プログラムが実行時に必要なメモリ量を常に知っているわけではないということです。

私はこのコードを持っています:

#include <stdio.h>

int main() {
   int r, c, i, j;
   printf("Rows?\n");
   scanf("%d", &r);
   printf("Columns?\n");
   scanf("%d", &c);

   int array[r][c];
   for (i = 0; i < r; i++)
      for (j = 0; j < c; j++)
         array[i][j] = Rand() % 100 + 1;

   return 0;
}

したがって、2D配列を作成したい場合は、1つを宣言して角かっこ内に数値を入力するだけです。しかし、このコードでは、ユーザーに行と列の数を尋ね、それらの変数で配列を宣言し、ランダムな整数で行と列を埋めました。

だから私の質問は:mallocのようなものをここで使用する必要がないのはなぜですか?実行時に入れる行と列の数がコードでわからないので、なぜ現在のコードでその配列にアクセスできるのですか?

3
c_help123

動的メモリ割り当て(malloc())のポイントは、実行時にサイズを提供できることではありませんが、それが重要な機能の1つでもあります。 動的メモリ割り当ての要点は、関数の戻り値を乗り越えたことです

オブジェクト指向のコードでは、次のような関数が表示されることがあります。

_Object* makeObject() {
    Object* result = malloc(sizeof(*result));
    result->someMember = ...;
    return result;
}
_

この作成者関数は、固定サイズのメモリを割り当て(sizeofはコンパイル時に評価されます!)、それを初期化し、割り当てを呼び出し元に返します。呼び出し元は、返されたポインタをどこにでも自由に格納でき、しばらくしてから別の関数を格納できます。

_void destroyObject(Object* object) {
    ...  //some cleanup
    free(object);
}
_

と呼ばれます。

これは自動割り当てでは不可能です。

_Object* makeObject() {
    Object result;
    result->someMember = ...;
    return &result;    //Wrong! Don't do this!
}
_

変数resultは、関数が呼び出し元に戻ると存在しなくなり、返されるポインターはぶら下がります。呼び出し元がそのポインターを使用すると、プログラムは未定義の動作を示し、ピンクの象が表示される場合があります。


また、コールスタックのスペースは通常かなり制限されています。ギガバイトのメモリをmalloc()に要求できますが、自動配列と同じ量を割り当てようとすると、プログラムがセグメンテーション違反を起こす可能性が高くなります。 これがmalloc()の2番目の理由です:大きなメモリオブジェクトを割り当てる手段を提供するため

あなたのプログラムでは、配列はスタックに自動ストレージ、別名で割り当てられます。これは、定義のスコープを離れると自動的に解放されます。関数の本体main。 C99で導入された、定義内の配列のサイズとして変数式を渡すこのメソッドは、可変長配列または[〜#〜] vla [〜#〜]

サイズが大きすぎる、または負の場合、定義は未定義の動作をします。たとえば、スタックオーバーフローが発生します。

このような潜在的な副作用を無効にするには、次元の値を確認して、mallocまたはcallocを使用します。

_#include <stdio.h>
#include <stdlib.h>

int main() {
    int r, c, i, j;

    printf("Rows?\n");
    if (scanf("%d", &r) != 1)
        return 1;
    printf("Columns?\n");
    if (scanf("%d", &c) != 1)
        return 1;

    if (r <= 0 || c <= 0) {
        printf("invalid matrix size: %dx%d\n", r, c);
        return 1;
    }
    int (*array)[c] = calloc(r, sizeof(*array));
    if (array == NULL) {
        printf("cannot allocate memory for %dx%d matrix\n", r, c);
        return 1;
    }
    for (i = 0; i < r; i++) {
        for (j = 0; j < c; j++) {
            array[i][j] = Rand() % 100 + 1;
        }
    }
    free(array);
    return 0;
}
_

int (*array)[c] = calloc(r, sizeof(*array));も可変長配列定義です。arrayc intsの配列へのポインターです。 sizeof(*array)sizeof(int[c])であり、実行時に_(sizeof(int) * c)_に評価されるため、マトリックスに割り当てられるスペースはsizeof(int) * c * rです。

0
chqrlie