11

グローバル変数のメモリはプログラムの起動時に割り当てられるのに対し、ローカル変数のメモリは関数呼び出しが行われるたびに割り当てられることを学びました。

ケース1:
サイズ63500000のグローバル整数配列を宣言しました。使用されているメモリは256MBです
。IdeoneLink

include <stdio.h>
int a[63500000];
int main()
{
    printf ("This code requires about 250 MB memory\n");
    return 0;
}

ケース2:
main()で同じサイズのローカル整数配列を宣言し、使用されるメモリは1.6MBの
Ideoneリンクです

#include <stdio.h>
int main()
{
    int a[63500000]= {1,5,0};
    printf ("This code requires only 1.6 MB \n");
    //printf ("%d\n", a[0]);
    return 0;
}

ケース3:
別の関数で同じサイズのローカル整数配列を宣言しました。使用されるメモリは1.6MBです
。IdeoneLink

#include <stdio.h>
void f()
{
    int a[63500000];
}

int main()
{
    f();
    return 0;
}

使用するメモリに違いがあるのか​​、メモリ割り当ての概念が間違っているのか説明してください。

4

3 に答える 3

25

まず第一に、ideoneコンパイラはGCCです。

それで、これをコンパイルするとき、GCCは何をしますか?:

void foo ()
{
  int a[63500000];
}

gcc -S -O2 foo.c生成:

foo:
    pushl   %ebp
    movl    %esp, %ebp
    popl    %ebp
    ret

つまり、スタックにはも割り当てられませ

アレイは使用されないため、GCCによって単純に最適化されます。

グローバルは別のコンパイルユニットで使用される可能性があるため、GCCはグローバルでこれを行いません。したがって、グローバルが使用されないかどうかはわかりません。また:グローバルはスタック上にありません(グローバルであるため)。

ここで、実際にローカル配列を使用するとどうなるかを見てみましょう。

int bar (int a, int b, int c)
{
  int f[63500000];
  f[a] = 9;
  f[b] = 7;
  return f[c];
}

物事は非常に異なります:

bar:
    pushl   %ebp
    movl    %esp, %ebp
    subl    $254000000, %esp
    movl    8(%ebp), %eax
    movl    $9, -254000000(%ebp,%eax,4)
    movl    12(%ebp), %eax
    movl    $7, -254000000(%ebp,%eax,4)
    movl    16(%ebp), %eax
    movl    -254000000(%ebp,%eax,4), %eax
    leave
    ret

この行:subl $254000000, %esp配列のサイズに対応します。つまり、メモリスタックに割り当てられます。

barさて、プログラムで関数を使おうとしたらどうなるでしょうか。

int bar (int a, int b, int c)
{
  int f[63500000];
  f[a] = 9;
  f[b] = 7;
  return f[c];
}

int main (void)
{
  return bar (0, 0, 0);
}

barこの関数がスタックに250メガバイト程度を割り当てることはすでに見てきました。私のデフォルトのGNU/Linuxインストールでは、スタックサイズは8MBに制限されています。そのため、プログラムを実行すると、「セグメンテーション違反」が発生します。シェルで以下を実行することにより、必要に応じて増やすことができます。

ulimit -s 1000000 #i.e. allow stack size to grow close to 1GB

次に、プログラムを実行できます。実際に実行されます。

ideone Webサイトで失敗する理由は、プログラムを実行するときにスタックサイズが制限されているためです(そうしないと、悪意のあるユーザーがシステムを台無しにする可能性があります)。

于 2012-08-15T21:34:58.490 に答える
8

ケース2、3

関数内で定義する変数は、スタックに割り当てられます。これは、関数が終了すると、関連付けられたメモリがクリーンアップされる(スタックが「ポップ」される)ことを意味します。

ケース1

グローバルスコープで定義された変数は、プロセスの存続期間中に存在するデータセグメント(または、通常、オペレーティングシステムから要求されたメモリスペース)に割り当てられます。

さらに

mallocを使用して割り当てられたメモリはヒープから割り当てられ、freeを使用して明示的に解放されるまで割り当てられたままになります。

最近のOSは、プログラムによって要求されたアドレススペースを提供する可能性がありますが、メモリ(またはページと呼ばれることが多いメモリの一部)が物理的にアクセスされるまで、RAMでそのアドレススペースを物理的に戻すことはできません。

于 2012-08-15T21:12:17.630 に答える
2

case 2Linuxではスタックが通常8MBである64MBのスタックメモリcase 3を要求しているため、スタックオーバーフローが発生します。これにより、ランダムに悪いことが発生したり、コアダンプやクラッシュが発生したりします。

この回答は、プロセスアドレス空間のさまざまなセクション(.text、.bss、.data)と、変数のさまざまな割り当てがどのように行われるかを大いに説明しています。

于 2012-08-15T21:23:43.007 に答える