74

今日、私は友人のCコードを手伝っていましたが、なぜそれが起こったのか説明できない奇妙な行動を見つけました。整数のリストと各行を含むTSVファイルがありましたint。最初の行は、リストに含まれていた行数でした。

また、非常に単純な「readfile」を含むacファイルもありました。最初の行は行数であると読み取られn、次に次の初期化が行われました。

int list[n]

nそして最後に。のforループfscanf

小さいn(〜100.000まで)の場合、すべてが正常でした。ただし、nが大きい場合(10 ^ 6)、セグメンテーション違反が発生することがわかりました。

最後に、リストの初期化を次のように変更しました

int *list = malloc(n*sizeof(int))

非常に大きい場合でも、すべてがうまくいきますn

誰かがこれが起こった理由を説明できますか?int list[n]使用を開始したときに停止したセグメンテーション違反の原因は何list = malloc(n*sizeof(int))ですか?

4

9 に答える 9

170

ここではいくつかの異なる作品が演奏されています。

1つ目は、配列を次のように宣言することの違いです。

int array[n];

int* array = malloc(n * sizeof(int));

最初のバージョンでは、自動保存期間を持つオブジェクトを宣言しています。これは、配列を呼び出す関数が存在する間だけ配列が存続することを意味します。2番目のバージョンでは、動的なストレージ期間のメモリを取得しています。これは、明示的に割り当てが解除されるまでメモリが存在することを意味しますfree

ここで2番目のバージョンが機能する理由は、Cが通常どのようにコンパイルされるかについての実装の詳細です。malloc通常、Cメモリは、スタック(関数呼び出しとローカル変数の場合)やヒープ( edオブジェクトの場合)を含むいくつかの領域に分割されます。スタックは通常、ヒープよりもはるかに小さいサイズです。通常は8MB程度です。その結果、で巨大な配列を割り当てようとすると

int array[n];

次に、スタックのストレージスペースを超えて、セグメンテーション違反が発生する可能性があります。一方、ヒープは通常、サイズが大きいため(たとえば、システムの空き容量と同じくらい)、malloc大きなオブジェクトを作成してもメモリ不足エラーは発生しません。

一般に、Cの可変長配列には注意してください。スタックサイズを簡単に超える可能性があります。mallocサイズが小さいことがわかっている場合や、アレイが本当に必要なのは短期間だけである場合を除いて、優先してください。

お役に立てれば!

于 2012-05-13T21:56:19.620 に答える
15
int list[n]

スタックn上の整数用のスペースを割り当てます。これは通常、かなり小さいです。スタックでメモリを使用することは、他の方法よりもはるかに高速ですが、非常に小さく、巨大な配列を割り当てたり、再帰を深くしすぎたりすると、スタックがオーバーフローしやすくなります(つまり、メモリの割り当てが多すぎます)。この方法で割り当てられたメモリの割り当てを手動で解除する必要はありません。配列がスコープ外になると、コンパイラによって割り当てが解除されます。

malloc一方、ヒープにはスペースが割り当てられます。これは通常、スタックと比較して非常に大きくなります。ヒープを使い果たすには、ヒープにはるかに大量のメモリを割り当てる必要がありますが、スタックにあるメモリよりもヒープにメモリを割り当てるのは非常に遅く、free使い終わったら手動で割り当てを解除する必要があります。 。

于 2012-05-13T21:56:13.580 に答える
3

int list [n]はデータをスタックに格納し、mallocはデータをヒープに格納します。

スタックは限られており、スペースはあまりありませんが、ヒープははるかに大きくなります。

于 2012-05-13T21:55:19.957 に答える
1

int list[n]はVLAであり、ヒープではなくスタックに割り当てます。解放する必要はなく(関数呼び出しの最後に自動的に解放されます)、すぐに割り当てられますが、ご存知のように、ストレージスペースは非常に限られています。ヒープに大きな値を割り当てる必要があります。

于 2012-05-13T21:55:26.617 に答える
1

この宣言は、スタックにメモリを割り当てます

    int list[n]

mallocはヒープに割り当てます。

スタックサイズは通常ヒープよりも小さいため、スタックに割り当てるメモリが多すぎると、スタックオーバーフローが発生します。

詳細については、この回答も参照してください

于 2012-05-13T21:55:31.373 に答える
1

実装に典型的な実装があると仮定すると、次のようになる可能性があります。

int list[n]

スタックに割り当てられたリスト。

int *list = malloc(n*sizeof(int))

ヒープに割り当てられたメモリ。

スタックの場合、通常、これらが成長できる大きさには制限があります(成長できる場合)。ヒープの場合でも制限がありますが、RAM +スワップ+アドレス空間によって大幅に(広く)制約される傾向があります。RAM+スワップ+アドレス空間は、通常、それ以上ではないにしても、少なくとも1桁大きくなります。

于 2012-05-13T21:56:35.753 に答える
1

Linuxを使用している場合は、ulimit -sをより大きな値に設定できます。これは、スタック割り当てでも機能する可能性があります。スタックにメモリを割り当てると、そのメモリは関数の実行が終了するまで残ります。(mallocを使用して)ヒープにメモリを割り当てると、いつでも(関数の実行が終了する前であっても)メモリを解放できます。

一般に、ヒープは大容量のメモリ割り当てに使用する必要があります。

于 2012-05-14T04:41:09.437 に答える
0

を使用して割り当てる場合malloc、メモリはスタックからではなくヒープから割り当てられます。スタックはサイズがはるかに制限されています。

于 2012-05-13T21:55:54.483 に答える
0
   int array[n];

これは静的に割り当てられた配列の例であり、コンパイル時に配列のサイズがわかります。そして、配列はスタックに割り当てられます。

   int *array(malloc(sizeof(int)*n);

これは動的に割り当てられた配列の例であり、配列のサイズは実行時にユーザーに認識されます。そして、配列はヒープに割り当てられます。

于 2016-07-31T14:22:19.937 に答える