私の変数はメモリのどこにCに保存されますか?


156

メモリがデータ、ヒープ、スタック、コードの4つのセグメントに分割されていることを考慮して、グローバル変数、静的変数、定数データ型、ローカル変数(関数で定義および宣言)、変数(メイン関数内)、ポインター、および動的に割り当てられたスペース(mallocおよびcallocを使用)はメモリに格納されますか?

次のように割り当てられると思います。

  • グローバル変数------->データ
  • 静的変数------->データ
  • 定数データ型----->コード
  • ローカル変数(関数で宣言および定義)--------> stack
  • メイン関数で宣言および定義された変数----->ヒープ
  • ポインタ(例えばchar *arrint *arr)------->ヒープ
  • 動的に割り当てられたスペース(mallocおよびcallocを使用)-------->スタック

これらの変数は、Cの観点からのみ参照しています。

私はCを初めて使用するので、間違っている場合は修正してください。


4
タイプはメモリに保存されません。

5
mainは別の機能です。malloc他の場所と同じようにしない限り、変数はスタックに置かれます。
simonc 2013年

4
ポインタは(通常)スタックに格納されます。それらが指すメモリ(通常はmalloc / callocを介して割り当てる)は、(通常)ヒープ上にあります。
jpm 2013年

3
動的に割り当てられた領域(malloc、callocを使用)-------->ヒープ
One Man Crew

3
main関数で宣言および定義された変数----->スタック
One Man Crew

回答:


216

あなたはこれらのいくつかを正しく理解しましたが、質問を書いた人は誰でも少なくとも1つの質問であなたをだましました:

  • グローバル変数------->データ(正しい)
  • 静的変数------->データ(正しい)
  • 定数データ型----->コードおよび/またはデータ。定数自体がデータセグメントに格納され、それへの参照がコードに埋め込まれるような状況では、文字列リテラルを検討してください
  • ローカル変数(関数で宣言および定義)-------->スタック(正しい)
  • 関数で宣言および定義された変数main-----> ヒープもスタックします(教師はあなたをだまそうとしました)
  • pointers(ex:char *arrint *arr)-------> コンテキストに応じてヒープデータまたはスタック。Cを使用すると、グローバルまたはstaticポインターを宣言できます。その場合、ポインター自体がデータセグメントに含まれます。
  • 動的に割り当てられたスペース(使用malloccallocrealloc)--------> スタックヒープ

「スタック」は正式には「自動ストレージクラス」と呼ばれていることに言及する価値があります。


6
また、ヒープは公式には何も呼び出されていないことにも言及する価値があります。割り当てられたメモリはどこかに由来しますが、その「どこかに」の規格には名前がありません。
スティーブジェソップ2013年

6
一部のシステム(つまり、Linuxおよび* BSD)では、とalloca同様にmalloc機能しますが、スタック割り当てを行います。
Andreas Grapentin 2013年

メソッド内で宣言されたconst変数はどこに行くのですか?
Mahori 2014年

@Ravi残りの定数と同じ場所(上記のポイント3)。
dasblinkenlight 2014年

私はGCC 4.8.1を使用していますが、DATAセグメントのメインに対してローカルなconst変数を格納していないようです。以下は、そのような3つのプログラムのコードとメモリマップです。コード1:int main(void){// char a [10] = "HELLO"; // 1 // const char a [10] = "HELLO"; // 2は0を返す; 上記のメモリーマップ:テキストデータbss dec hex filename 7264 1688 1040 9992 2708 a.exe MEMORY MAP FOR 2:text data bss dec hex filename 7280 1688 1040 10008 2718 a.exe MEMORY MAP FOR 3:text data bss dec hex filename 7280 1688 1040 10008 2718 A.EXE
Mahori

124

それらのメモリセグメントについて知りたいと思うかもしれない将来の訪問者のために、私はCの5つのメモリセグメントに関する重要なポイントを書いています。

いくつかのヘッドアップ:

  1. Cプログラムが実行されるたびに、RAMにプログラム実行用のメモリが割り当てられます。このメモリは、頻繁に実行されるコード(バイナリデータ)、プログラム変数などを格納するために使用されます。以下のメモリセグメントは同じことを示しています。
  2. 通常、変数には3つのタイプがあります。
    • ローカル変数(Cでは自動変数とも呼ばれます)
    • グローバル変数
    • 静的変数
    • グローバル静的またはローカル静的変数を使用できますが、上記の3つは親タイプです。

Cの5つのメモリセグメント:

1.コードセグメント

  • コードセグメントは、テキストセグメントとも呼ばれ、頻繁に実行されるコードを含むメモリ領域です。
  • 多くの場合、コードセグメントは読み取り専用であり、バッファオーバーフローなどのプログラミングバグによってオーバーライドされるリスクを回避します。
  • コードセグメントには、ローカル変数(Cでは自動変数とも呼ばれます)、グローバル変数などのプログラム変数は含まれていません。
  • C実装に基づいて、コードセグメントには読み取り専用の文字列リテラルも含めることができます。たとえば、実行するとprintf("Hello, world")、文字列「Hello、world」がコード/テキストセグメントに作成されます。これsizeは、Linux OSのコマンドを使用して確認できます。
  • 参考文献

データセグメント

データセグメントは以下の2つの部分に分かれており、通常はヒープ領域の下、またはスタックの上の一部の実装にありますが、データセグメントがヒープ領域とスタック領域の間にあることはありません。

2.初期化されていないデータセグメント

  • このセグメントは、bssとも呼ばれます。
  • これは、以下を含むメモリの一部です。
    1. 初期化されていないグローバル変数 (ポインター変数を含む)
    2. 初期化されていない定数グローバル変数
    3. 初期化されていないローカル静的変数
  • 初期化されていないグローバルまたは静的ローカル変数は、初期化されていないデータセグメントに格納されます
  • 例:グローバル変数int globalVar;または静的ローカル変数static int localStatic;は、初期化されていないデータセグメントに格納されます。
  • グローバル変数を宣言してそれを初期化した0場合NULL、それでも初期化されていないデータセグメントまたはbssに移動します。
  • 参考文献

3.初期化されたデータセグメント

  • このセグメントは以下を保管します。
    1. 初期化されたグローバル変数 (ポインター変数を含む)
    2. 初期化された定数グローバル変数
    3. 初期化されたローカル静的変数
  • たとえば、グローバル変数int globalVar = 1;または静的ローカル変数static int localStatic = 1;は、初期化されたデータセグメントに格納されます。
  • このセグメントは、初期化された読み取り専用領域と初期化された読み取り/書き込み領域にさらに分類できます。初期化された定数グローバル変数は初期化された読み取り専用領域に入り、実行時に値を変更できる変数は初期化された読み取り/書き込み領域に入ります。
  • このセグメントのサイズは、プログラムのソースコードの値のサイズによって決まり、実行時に変更されません
  • 参考文献

4.スタックセグメント

  • スタックセグメントは、関数内で作成された変数を格納するために使用されます(関数はメイン関数またはユーザー定義関数の可能性があります)。
    1. 関数のローカル変数(ポインター変数を含む)
    2. 関数に渡される引数
    3. 差出人住所
  • スタックに格納されている変数は、関数の実行が終了するとすぐに削除されます。
  • 参考文献

5.ヒープセグメント

  • このセグメントは、動的メモリ割り当てをサポートするためのものです。プログラマがCに動的にし、いくつかのメモリを割り当てるしたい場合には、使用して行われmalloccallocまたはrealloc方法を。
  • たとえばint* prt = malloc(sizeof(int) * 2)、8バイトがヒープに割り当てられると、その場所のメモリアドレスが返され、ptr変数に格納されます。ptr変数は、それが宣言されている/使用方法に応じてスタック又はデータセグメントのいずれかであろう。
  • 参考文献

3.初期化されたデータセグメントで初期化されていないのではなく、初期化されるべきではありません。
Suraj Jain

「初期化されていないデータ・セグメントに格納されている」(複数インスタンス):あなたが意味するか、「データ・セグメントの初期化されていない保存されましたか」
Peter Mortensen 2017

@PeterMortensen私は両方のことを意味します。「初期化されていないグローバル変数または静的ローカル変数は、初期化されていないデータセグメントに格納されます」
hagrawal

Cでグローバル静的変数をどのように持つことができますか?

「いくつかの注意事項」の下で、「グローバル静的変数またはローカル静的変数を持つことができますが、上記の3つは親型です」というこの点を見つけました。ここでは、「グローバルスタティック」という用語を使用しています。私のポイントは、静的変数はグローバルにすることはできないということです。つまり、変数をグローバルにする必要がある場合は、プログラムの実行が完了するまで変数にアクセスできる必要があります。私が間違っている場合は説明して助けてください。

11

あなたの間違った文章を修正しました

constant data types ----->  code //wrong

ローカル定数変数----->スタック

初期化されたグローバル定数変数----->データセグメント

初期化されていないグローバル定数変数-----> bss

variables declared and defined in main function  ----->  heap //wrong

メイン関数で宣言および定義された変数----->スタック

pointers(ex:char *arr,int *arr) ------->  heap //wrong

dynamically allocated space(using malloc,calloc) --------> stack //wrong

pointers(ex:char * arr、int * arr)------->そのポインタ変数のサイズはスタックに入れられます。

mallocまたはを使用してcalloc)nバイトのメモリを動的に割り当て、それを指すようにポインタ変数を作成するとします。これnでメモリのバイトはヒープにあり、ポインタ変数は4バイト(64ビットマシンの場合は8バイト)を要求し、スタックの開始ポインタを格納するためにn、メモリチャンクのバイトの。

注:ポインター変数は、任意のセグメントのメモリーを指すことができます。

int x = 10;
void func()
{
int a = 0;
int *p = &a: //Now its pointing the memory of stack
int *p2 = &x; //Now its pointing the memory of data segment
chat *name = "ashok" //Now its pointing the constant string literal 
                     //which is actually present in text segment.
char *name2 = malloc(10); //Now its pointing memory in heap
...
}

動的に割り当てられた領域(malloc、callocを使用)-------->ヒープ


ポインターは、スタックまたはヒープのいずれかに
置く

@airza:更新されました。実際、私はその詳細のみを更新していました:)
rashok '29年

次のメモリマップで、スタックとヒープの場所を指摘していただけますか?スタックとメモリは実行時にのみ適用される可能性があるため、これが正しい質問かどうかはわかりません。メモリマップ: "テキストデータBSS 12月の六角ファイル名7280 1688 1040 10008 2718 A.EXE"
Mahori

7

一般的なデスクトップアーキテクチャは、プロセスの仮想メモリをいくつかのセグメントに分割します。

  • テキストセグメント:実行可能コードが含まれています。命令ポインタはこの範囲の値を取ります。

  • データセグメント:グローバル変数が含まれます(つまり、静的リンケージを持つオブジェクト)。読み取り専用データ(文字列定数など)と初期化されていないデータ( "BSS")に細分されます。

  • スタックセグメント:プログラムの動的メモリ、つまり、フリーストア( "ヒープ")とすべてのスレッドのローカルスタックフレームが含まれます。従来、CスタックとCヒープは、両端からスタックセグメントに成長するのに使用されていましたが、あまりにも安全でないため、実践は中止されたと思います。

Cプログラムは通常、静的ストレージ期間を持つオブジェクトをデータセグメントに配置し、動的に割り当てられたオブジェクトをフリーストアに配置し、自動オブジェクトをそれが存在するスレッドの呼び出しスタックに配置します。

古いx86リアルモードや組み込みデバイスなどの他のプラットフォームでは、状況が根本的に異なる場合があります。


「安全性が低すぎるため、この慣行は放棄されたと思います」-スレッドの実装が不可能になるため、プログラムごとに複数のスタックが必要になり、すべてを最後に置くことはできません:-)
Steve Jessop

@SteveJessop:はい、私もそう考えていました。しかし、スレッドは長い間存在していました-すべてのスレッドスタックも逆方向に成長したのか、それともヒープのように成長したのかはわかりません...とにかく、今日、すべてが同じ方向に進んでいて、ガードがありますページ。
Kerrek SB、2013

6

これらの変数は、Cの観点からのみ参照しています。

C言語の観点からすると、重要なのは、範囲、スコープ、リンケージ、およびアクセスです。アイテムが異なるメモリセグメントにどのようにマップされるかは、個々の実装次第であり、それは異なります。言語の標準は、メモリ・セグメントについて話しませんすべてで。ほとんどの最新のアーキテクチャはほとんど同じように動作します。ブロックスコープ変数と関数引数はスタックから割り当てられ、ファイルスコープと静的変数はデータまたはコードセグメントから割り当てられ、動的メモリはヒープから割り当てられ、一部の定数データは読み取り専用セグメントに格納されます、など


3

ストレージについて留意する必要があることの1つは、as-ifルールです。コンパイラは、変数を特定の場所に置く必要はありません。代わりに、コンパイルされたプログラムが抽象Cマシンの規則に従って抽象Cマシンで実行されたかのように動作する限り、好きな場所に変数を配置できます。これはすべての保存期間に適用されます。例えば:

  • すべてにアクセスされない変数は完全に削除できます-どこにもストレージがありません。 - 42生成されたアセンブリコードにどのようにあるかを確認しますが、兆候はありません404
  • アドレスが取得されない自動保存期間を持つ変数は、メモリにまったく保存する必要はありません。例として、ループ変数があります。
  • メモリーにある、constまたは効果的にconst必要ない変数。 -コンパイラは、それfooが効果的であることを証明しconst、その使用をコードにインライン化できます。bar外部リンケージがあり、コンパイラーは現在のモジュールの外部で変更されないことを証明できないため、インライン化されません。
  • で割り当てられたオブジェクトはmalloc、ヒープから割り当てられたメモリに存在する必要はありません。 -コードがどのように呼び出しmallocも行わず、値42もメモリに保存されていないことに注意してください。値はレジスタに保持されます。
  • したがって、割り当てられたオブジェクトとmalloc参照は、メモリをリークするfree 必要がないオブジェクトの割り当てを解除せずに失われます...
  • によって割り当てられたオブジェクトは、Unixenのプログラムブレーク()のmallocヒープ内にある必要はありません...sbrk(0)

1

ポインタ(ex:char * arr、int * arr)------->ヒープ

いいえ、スタックまたはデータセグメントに配置できます。彼らはどこを指すことができます。


main動的に割り当てられた変数に関するステートメントも間違っています
simonc

スタックまたはデータセグメントだけではありません。ポインターの配列を指すポインターを考えてください。この場合、配列内のポインターはヒープに格納されます。
Sebi2020、2015年

1
  • 変数/自動変数--->スタックセクション
  • 動的に割り当てられる変数--->ヒープセクション
  • 初期化されたグローバル変数->データセクション
  • 初期化されていないグローバル変数->データセクション(bss)
  • 静的変数->データセクション
  • 文字列定数->テキストセクション/コードセクション
  • 関数->テキストセクション/コードセクション
  • テキストコード->テキストセクション/コードセクション
  • レジスタ-> CPUレジスタ
  • コマンドライン入力->環境/コマンドラインセクション
  • 環境変数->環境/コマンドラインセクション

環境/コマンドラインセクションとは何ですか?Linuxに存在しますか?
Haoyuan Ge 2016

-1

逆アセンブリ分析を使用したLinux最小実行可能例

これは標準で指定されていない実装の詳細なので、コンパイラが特定の実装で何をしているかを見てみましょう。

この回答では、分析を行う特定の回答にリンクするか、ここに分析を直接提供し、すべての結果をここに要約します。

これらはすべてさまざまなUbuntu / GCCバージョンにあり、結果はバージョン間でかなり安定している可能性がありますが、バリエーションが見つかった場合は、より正確なバージョンを指定してみましょう。

関数内のローカル変数

それmainか他の機能か:

void f(void) {
    int my_local_var;
}

に示されているように、gdbで<valueoptimized out>はどういう意味ですか?

  • -O0:スタック
  • -O3:流出しない場合は登録し、それ以外の場合はスタックします

スタックが存在する理由の動機については、x86アセンブリのレジスターで使用されるプッシュ/ポップ命令の機能は何ですか?を参照してください

グローバル変数とstatic関数変数

/* BSS */
int my_global_implicit;
int my_global_implicit_explicit_0 = 0;

/* DATA */
int my_global_implicit_explicit_1 = 1;

void f(void) {
    /* BSS */
    static int my_static_local_var_implicit;
    static int my_static_local_var_explicit_0 = 0;

    /* DATA */
    static int my_static_local_var_explicit_1 = 1;
}
  • 初期化されている0か、初期化されていない場合(したがって暗黙的にに初期化されている場合0):.bssセクション、以下も参照してください:.bssセグメントが必要な理由
  • それ以外の場合:.dataセクション

char * そして char c[]

に示されているように:CおよびC ++の静的変数はどこに格納されていますか?

void f(void) {
    /* RODATA / TEXT */
    char *a = "abc";

    /* Stack. */
    char b[] = "abc";
    char c[] = {'a', 'b', 'c', '\0'};
}

TODO非常に大きな文字列リテラルもスタックに入れられますか?または.data?それともコンパイルは失敗しますか?

関数の引数

void f(int i, int j);

関連する呼び出し規約を通過する必要があります。例:https : //en.wikipedia.org/wiki/X86_calling_conventions X86の場合はをます。これは、各変数の特定のレジスターまたはスタックの場所を指定します。

次に、「gdbでの<最適化された値>の意味」に示されているように-O0しながら、スタックにすべてをslurps -O3試行を極力レジスタを使用します。

ただし、関数がインライン化された場合は、通常のローカルと同様に扱われます。

const

あなたはそれをタイプキャストすることができるので、それは何の違いもないと信じています。

逆に、コンパイラーが一部のデータが書き込まれていないと判断できる場合、理論的には .rodataはconstでなくます。

TODO分析。

ポインタ

これらは変数であり(数値であるアドレスを含みます)、残りすべてと同じです:-)

malloc

は関数なmallocので、この質問はあまり意味がありませんmalloc

int *i = malloc(sizeof(int));

*i はアドレスを含む変数なので、上記のケースに該当します。

mallocが内部でどのように機能するかについては、それを呼び出すと、Linuxカーネルが特定のアドレスを内部データ構造に書き込み可能としてマークし、プログラムが最初にそれらに触れたときに障害が発生し、カーネルがページテーブルを有効にして、アクセスを許可しますsegfaulなしで発生:x86ページングは​​どのように機能しますか?

注ただし、これはまさに、基本的であることをexecあなたが実行可能ファイルを実行しようとすると、システムコールは、ボンネットの下に行われます。それがロードしたいことマークページ、およびそこにプログラムを書き込み、また、以下を参照してください下を実行しているどのようにカーネルのget実行可能なバイナリファイルをLinux?ただし、execロード先に追加の制限がある(ただし、コードが再配置可能ではないなど)。

使用される正確なsyscall mallocmmap、現代の2020実装であり、過去brkに使用されていました。malloc()はbrk()またはmmap()を使用しますか?

動的ライブラリ

基本的mmapにメモリを確保する:https : //unix.stackexchange.com/questions/226524/what-system-call-is-used-to-load-libraries-in-linux/462710#462710

envinroment変数やmainさんargv

初期スタックの上:https : //unix.stackexchange.com/questions/75939/where-is-the-environment-string-actual-stored TODOなぜ.dataにないのですか?

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.