web-dev-qa-db-ja.com

Cのmalloc()とcalloc()について非常に混乱しています

私はいつもJavaでプログラミングしてきたので、おそらくこれについて混乱しているのでしょう。

Java私はポインタを宣言します:

_int[] array
_

初期化するか、メモリを割り当てます。

_int[] array = {0,1,0}
int[] array = new int[3]
_

さて、Cでは、それはすべてとても混乱しています。最初は、宣言するのと同じくらい簡単だと思いました。

_int array[]
_

それを初期化するか、メモリを割り当てます:

_int array[] = {0,1,0}
int array[] = malloc(3*sizeof(int))
int array[] = calloc(3,sizeof(int))
_

間違っていない限り、上記のすべては同等のJava-Cです。

その後、今日、次のコードを見つけました。

_pthread_t tid[MAX_OPS];
_

以下のいくつかの行、初期化の種類なし...

_pthread_create(&tid[0],NULL,mou_usuari,(void *) 0);
_

驚いたことに(少なくとも私には)、コードは機能します!少なくともJavaでは、それはNice "NullPointerException"を返します!

だから、順番に:

  1. Java-Cのすべての「翻訳」は正しいですか?

  2. なぜそのコードが機能するのですか?

  3. malloc(n*sizeof(int))calloc(n,sizeof(int))の使用に違いはありますか?

前もって感謝します

27
bluehallu

配列にメモリを割り当てることはできません。配列の寿命全体にわたって、配列のサイズは固定されています。配列をnullにすることはできません。配列はポインターではありません。

mallocは、プログラム用に予約されているメモリブロックのアドレスを返します。それを(メモリブロックである)配列に「割り当てる」ことはできませんが、このメモリブロックのアドレスをポインタに保存できます。幸いなことに、配列サブスクリプションはポインタを介して定義されます。 、例えば.

int *ptr = malloc(5 * sizeof *ptr);
ptr[2] = 5; // access the third element "of ptr"
free(ptr); // always free at the end

サイズなしで配列を宣言する場合(つまり、array[])、単に配列のサイズが初期化リストから決定されることを意味します。あれは

int array[] = {1, 2, 3, 4, 5}; // is equal to
int array[5] = {1, 2, 3, 4, 5};

サイズや初期化子なしで配列を宣言しようとするとエラーになります。


コード pthread_t tid[MAX_OPS];は、tidという名前の配列pthread_tおよびサイズMAX_OPS

配列に自動ストレージがある場合(つまり、宣言が関数内にあり、静的ではなく、グローバルではない場合)、配列要素のそれぞれに不定値があります(そのような値を読み取ろうとする未定義の動作が発生します)。幸いなことに、関数呼び出しは、配列の最初の要素のアドレスを最初のパラメーターとして受け取り、おそらく関数内でそれ(要素)を初期化するだけです。


callocmallocの違いは、callocが返すメモリブロックがゼロに初期化されることです。あれは;

int *ptr = calloc(5, sizeof *ptr);
// is somewhat equal to
int *ptr = malloc(5 * sizeof *ptr);
memset(ptr, 0, 5 * sizeof *ptr);

の違い

int *ptr = malloc(5 * sizeof *ptr);
// and
int array[5];

arrayには自動ストレージがあり(スタックに格納され)、スコープ外になった後に「解放」されます。ただし、ptr(ヒープに格納される)は動的に割り当てられ、プログラマがfreedにする必要があります。

38
eq-

3つの非常に基本的で厳格な(そして誤解を招く!)Cトピックが欠落しています。

  • 配列とポインターの違い
  • 静的割り当てと動的割り当ての違い
  • スタックまたはヒープで変数を宣言することとの違い

int array[] = malloc(3*sizeof(int));と書くと、コンパイルエラーが発生します(次のようなものです) 'identifier':配列の初期化には中括弧が必要です)。

これは、配列を宣言すると静的な初期化のみが許可されることを意味します。

  • _int array[] = {1,2,3};_は、スタック上で3つの連続した整数を予約します。
  • _int array[3] = {1,2,3};_これは前のものと同じです。
  • _int array[3];_は、スタック上で3つの連続した整数を予約しますが、それらを初期化しません(内容はランダムなゴミになります)
  • _int array[4] = {1,2,3};_初期化子リストがすべての要素を初期化しない場合、残りは0に設定されます(C99§6.7.8/ 19):この場合、1,2,3,0を取得します

これらのすべての場合、allocating new memoryではなく、すでにスタックにコミットされているメモリを使用していることに注意してください。問題が発生するのは、スタックがいっぱいの場合のみです(推測すると、スタックオーバーフロー)。このため、_int array[];_の宣言は間違っており、意味がありません。

mallocを使用するには、ポインターを宣言する必要があります:_int* array_。

int* array = malloc(3*sizeof(int));を記述するとき、実際には3つの操作を実行しています。

  1. _int* array_は、スタック(メモリアドレスを含む整数変数)にポインターを予約するようコンパイラーに指示します
  2. malloc(3*sizeof(int))はヒープ3連続整数に割り当て、最初の整数を返します
  3. _=_は、戻り値(割り当てた最初の整数のアドレス)をポインター変数にコピーします

だから、あなたの質問に戻るには:

_pthread_t tid[MAX_OPS];
_

はスタック上の配列であるため、割り当てる必要はありません(_MAX_OPS_が16の場合、16 pthread_tを収めるために必要な連続したバイト数をスタック上で予約します)。このメモリのコンテンツはガベージになります(スタック変数はゼロに初期化されません)が、_pthread_create_はその最初のパラメーター(_pthread_t_変数へのポインター)に値を返し、以前のコンテンツを無視します。コードは問題ありません。

4
lornova

Cは、静的メモリ割り当てと動的メモリ割り当てを提供します。スタックから、またはコンパイラが管理する実行可能メモリに配列を割り当てることができます。これは、Javaでスタックにintを割り当てたり、ヒープにIntegerを割り当てたりする方法とまったく同じです。 Cの配列は、他のスタック変数とまったく同じです。範囲外になるなど。

{}とmalloc/callocの主な違いは、{}配列が静的に割り当てられ(解放する必要がない)、自動的に初期化されるのに対して、malloc/calloc配列は明示的に解放する必要があり、明示的に初期化する必要があることです。しかし、もちろん、malloc/calloc配列は範囲外にならず、(場合によっては)realloc()することができます。

1
Puppy

2-この配列宣言は静的です:

pthread_t tid[MAX_OPS];

動的割り当ての代わりに、メモリブロックを割り当てる必要はありません。

pthread_t *tid = (pthread_t *)malloc( MAX_OPS * sizeof(pthread_t) );

メモリを解放することを忘れないでください:

free(tid);

3-mallocとcallocの違いは、callocが配列にメモリブロックを割り当て、すべてのビットを0に初期化することです。

0
remy_jourde