CおよびC ++の静的変数はどこに保存されますか?


180

実行可能ファイルのどのセグメント(.BSS、.DATA、その他)に静的変数が格納されているため、名前の衝突が発生しませんか?例えば:


foo.c:                         bar.c:
static int foo = 1;            static int foo = 10;
void fooTest() {               void barTest() {
  static int bar = 2;            static int bar = 20;
  foo++;                         foo++;
  bar++;                         bar++;
  printf("%d,%d", foo, bar);     printf("%d, %d", foo, bar);
}                              }

両方のファイルをコンパイルして、fooTest()とbarTestを繰り返し呼び出すメインにリンクすると、printfステートメントは個別に増分します。fooおよびbar変数は翻訳単位に対してローカルであるため、理にかなっています。

しかし、ストレージはどこに割り当てられていますか?

明確にするために、ELF形式でファイルを出力するツールチェーンがあることを前提としています。したがって、これらの静的変数のために実行可能ファイルに予約されたスペース必要になる思います。 議論のために、GCCツールチェーンを使用すると仮定します。


1
ほとんどの人は、あなたの質問に答えるのではなく.DATAセクションに保存する必要があると言っています:.DATAセクションの正確な場所と、どこで見つけるか。あなたはすでに回答にマークを付けているので、あなたはすでにそれを見つける方法を知っていますか?
lukmac 2011年

初期化と非初期化が異なるセクションに配置されている理由:linuxjournal.com/article/1059
mhk

1
実行時にグローバル/静的変数に割り当てられるストレージは、名前解決とは関係がなく、ビルド/リンク時に発生します。実行可能ファイルがビルドされた後-名前はありません。
valdo 2014年

2
この質問は意味がなく、エクスポートされていないシンボルの「名前の衝突」は存在する可能性があるという誤った前提に基づいて構築されています。正当な質問がないという事実は、いくつかの答えがどれほど悲惨であるかを説明するかもしれません。信じがたいので、これを得た人はほとんどいません。
underscore_d

回答:


131

静力学がどこに行くかは、それらがゼロで初期化されているかどうかによって異なりますゼロ初期化された静的なデータになります(ブロックは、シンボルによって開始).bssセクション非ゼロ初期化されたデータは、に行く.DATA


50
「0以外で初期化された」とは、おそらく「初期化されたが、0以外のもので」を意味します。C / C ++には「初期化されていない」静的データなどは存在しないからです。staticはすべてデフォルトでゼロで初期化されます。
AnTは

21
@Don Neufeld:あなたの答えはまったく質問に答えません。なぜ受け入れられるのかわかりません。「foo」と「bar」の両方が0以外で初期化されているためです。問題は、同じ名前の2つの静的/グローバル変数を.bssまたは.dataのどこに配置するかです
lukmac

明示的にゼロで初期化された静的データが入る実装.dataと、初期化子のない静的データが入る実装を使用しました.bss
MM

1
@MM私の場合、静的メンバーが初期化されていない(暗黙的に0に初期化されている)か、明示的に0に初期化されているかにかかわらず、どちらの場合も.bssセクションに追加されます。
cbinder 2015

この情報は、特定の実行可能ファイルタイプに固有ですか?あなたが指定しなかったので、それは少なくともELFとWindows PE実行可能ファイルに適用されると思いますが、他のタイプはどうでしょうか?
ジェリージェレミア

116

プログラムがメモリに読み込まれると、さまざまなセグメントに編成されます。セグメントの1つはDATAセグメントです。データセグメントはさらに2つの部分に分割されます。

初期化されたデータセグメント:すべてのグローバルデータ、静的データ、および定数データが​​ここに格納されます。未
初期化データセグメント(BSS):すべての未初期化データはこのセグメントに格納されます。

この概念を説明する図は次のとおりです。

ここに画像の説明を入力してください


ここにこれらの概念を説明する非常に良いリンクがあります:

http://www.inf.udec.cl/~leo/teoX.pdf


上記の答えは、初期化された0がBSSに入ると述べています。初期化された0は、初期化されていないことを意味しますか、それとも0自体を意味しますか?それ自体が0を意味する場合は、それを回答に含める必要があります。
Viraj

定数データは.dataセグメントではなく、テキストセクションの.constセグメント内に格納されます。
user10678 2017年

これの代わりに( " 初期化されたデータセグメント:すべてのグローバル、静的、および定数データが​​ここに格納されます。未初期化データセグメント(BSS):すべての未初期化データがこのセグメントに格納されます。")、これは次のように言うべきだと思います:( " 初期化されたデータセグメント:ゼロ以外の値に初期化されたすべてのグローバル変数と静的変数、およびすべての定数データが​​ここに格納されます。初期化されていないデータセグメント(BSS):初期化されていない、または初期化されたすべてのグローバル変数と静的変数ゼロになり、このセグメントに格納されます。」)。
ガブリエルステープルズ

また、私が理解している限り、「初期化されたデータ」は初期化された変数 定数で構成できることに注意してください。マイクロコントローラ(例:STM32)で、初期化された変数は、デフォルトで保存されているフラッシュメモリと、起動時にRAMにコピーされ、初期化定数は、中に残って、そしてから読み取ることが意図されているFlashが唯一と一緒に、テキストが含まれ、プログラム自体であり、Flashのみに
ガブリエルステープルズ2018年

したがって、この図から収集しているのは、グローバル変数または静的変数(静的変数は期間がグローバル変数のように機能するため)はヒープにスタックにもなく、どちらにもないメモリに割り当てられるということです。そうですか?STM32リンカースクリプトをもう一度見て、メモリの割り当てをさらに調査できると思います。
ガブリエルステープルズ

32

実際、変数はタプル(ストレージ、スコープ、タイプ、アドレス、値)です。

storage     :   where is it stored, for example data, stack, heap...
scope       :   who can see us, for example global, local...
type        :   what is our type, for example int, int*...
address     :   where are we located
value       :   what is our value

ローカルスコープは、定義されている場所に応じて、翻訳単位(ソースファイル)、関数、またはブロックのいずれかにローカルであることを意味します。変数を複数の関数から見えるようにするには、変数をDATAまたはBSS領域に配置する必要があります(明示的に初期化されているかどうかに応じて)。そのスコープは、ソースファイル内のすべての関数または関数に応じてスコープされます。


21

データの保存場所は実装に依存します。

ただし、静的の意味は「内部リンケージ」です。したがって、シンボルはコンパイル単位(foo.c、bar.c)の内部にあり、そのコンパイル単位の外部で参照することはできません。したがって、名前の衝突はありません。


番号。static keyworldの意味はオーバーロードされています。このような場合、staticはストレージ修飾子であり、リンケージ修飾子ではありません。
ugasoft 2008

4
ugasoft:関数の外側の静的要素はリンケージ修飾子であり、内側は衝突のないストレージ修飾子です。
wnoise 2008

12

「グローバルで静的」な領域:)

C ++にはいくつかのメモリ領域があります。

  • ヒープ
  • 無料店
  • スタック
  • グローバル&スタティック
  • const

質問の詳細な回答については、こちらをご覧ください。

以下は、C ++プログラムの主要な個別のメモリー領域を要約したものです。一部の名前(たとえば、「ヒープ」)は、ドラフト[標準]ではそのように表示されないことに注意してください。

     Memory Area     Characteristics and Object Lifetimes
     --------------  ------------------------------------------------

     Const Data      The const data area stores string literals and
                     other data whose values are known at compile
                     time.  No objects of class type can exist in
                     this area.  All data in this area is available
                     during the entire lifetime of the program.

                     Further, all of this data is read-only, and the
                     results of trying to modify it are undefined.
                     This is in part because even the underlying
                     storage format is subject to arbitrary
                     optimization by the implementation.  For
                     example, a particular compiler may store string
                     literals in overlapping objects if it wants to.


     Stack           The stack stores automatic variables. Typically
                     allocation is much faster than for dynamic
                     storage (heap or free store) because a memory
                     allocation involves only pointer increment
                     rather than more complex management.  Objects
                     are constructed immediately after memory is
                     allocated and destroyed immediately before
                     memory is deallocated, so there is no
                     opportunity for programmers to directly
                     manipulate allocated but uninitialized stack
                     space (barring willful tampering using explicit
                     dtors and placement new).


     Free Store      The free store is one of the two dynamic memory
                     areas, allocated/freed by new/delete.  Object
                     lifetime can be less than the time the storage
                     is allocated; that is, free store objects can
                     have memory allocated without being immediately
                     initialized, and can be destroyed without the
                     memory being immediately deallocated.  During
                     the period when the storage is allocated but
                     outside the object's lifetime, the storage may
                     be accessed and manipulated through a void* but
                     none of the proto-object's nonstatic members or
                     member functions may be accessed, have their
                     addresses taken, or be otherwise manipulated.


     Heap            The heap is the other dynamic memory area,
                     allocated/freed by malloc/free and their
                     variants.  Note that while the default global
                     new and delete might be implemented in terms of
                     malloc and free by a particular compiler, the
                     heap is not the same as free store and memory
                     allocated in one area cannot be safely
                     deallocated in the other. Memory allocated from
                     the heap can be used for objects of class type
                     by placement-new construction and explicit
                     destruction.  If so used, the notes about free
                     store object lifetime apply similarly here.


     Global/Static   Global or static variables and objects have
                     their storage allocated at program startup, but
                     may not be initialized until after the program
                     has begun executing.  For instance, a static
                     variable in a function is initialized only the
                     first time program execution passes through its
                     definition.  The order of initialization of
                     global variables across translation units is not
                     defined, and special care is needed to manage
                     dependencies between global objects (including
                     class statics).  As always, uninitialized proto-
                     objects' storage may be accessed and manipulated
                     through a void* but no nonstatic members or
                     member functions may be used or referenced
                     outside the object's actual lifetime.

12

衝突はないと思います。ファイルレベルで(関数の外で)staticを使用すると、変数は現在のコンパイル単位(ファイル)に対してローカルとしてマークされます。現在のファイルの外には表示されないため、外部で使用できる名前を付ける必要はありません。

内部で静的を使用する関数することは異なります-変​​数は(静的かどうかにかかわらず)関数からのみ可視であり、その値はその関数への呼び出し間で保持されるだけです。

実際、staticは、場所によって2つの異なる処理を実行します。では、両方のしかし例、変数の可視性は、リンク時に使用すると、簡単に名前空間の衝突を防ぐことができますように制限されています。

そうは言っても、DATAセクションは0以外の値に初期化される変数を持つ傾向があるセクションに格納されると思います。これはもちろん、実装の詳細であり、標準で義務付けられているものではありません。動作のみが考慮され、内部でどのように行われるかは考慮されません。


1
@paxdiablo:2種類の静的変数について説明しました。この記事(en.wikipedia.org/wiki/Data_segment)はどちらを参照していますか?データセグメントはグローバル変数も保持します(静的変数とは性質がまったく逆です)。So, how does a segment of memory (Data Segment) store variables that can be accessed from everywhere (global variables) and also those which have limited scope (file scope or function scope in case of static variables)?
Lazer、2010

@eSKay、それは可視性と関係があります。コンパイルユニットのローカルなセグメントに格納されているものもあれば、完全にアクセス可能なものもあります。1つの例:DATAセグメントにブロックを提供する各comp-unitを考えます。それはすべてがそのブロックのどこにあるかを知っています。また、他のcomp-unitがアクセスできるようにしたいブロックのアドレスを公開します。リンカはリンク時にこれらのアドレスを解決できます。
paxdiablo 2010

11

自分で見つける方法 objdump -Sr

何が起こっているのかを実際に理解するには、リンカーの再配置を理解する必要があります。触れたことがない場合は、まずこの投稿を読んでみてください。

Linux x86-64 ELFの例を分析して、自分で見てみましょう。

#include <stdio.h>

int f() {
    static int i = 1;
    i++;
    return i;
}

int main() {
    printf("%d\n", f());
    printf("%d\n", f());
    return 0;
}

コンパイル:

gcc -ggdb -c main.c

次のコードでコードを逆コンパイルします。

objdump -Sr main.o
  • -S 元のソースが混在するコードを逆コンパイルします
  • -r 再配置情報を表示します

f私たちの逆コンパイルの内部を見ます:

 static int i = 1;
 i++;
4:  8b 05 00 00 00 00       mov    0x0(%rip),%eax        # a <f+0xa>
        6: R_X86_64_PC32    .data-0x4

そして、.data-0x4それは.dataセグメントの最初のバイトに行くと言います。

これ-0x4は、RIP相対アドレッシングを使用しているため、そこに%ripありR_X86_64_PC32ます。

これは、RIPが次の命令をポイントするために必要です。この命令は4バイトで始まり、その後00 00 00 00に再配置されます。これについては、https//stackoverflow.com/a/30515926/895245で詳しく説明しています

次に、ソースを変更i = 1して同じ分析を行うと、次のように結論付けます。

  • static int i = 0 に行く .bss
  • static int i = 1 に行く .data


6

使用しているプラ​​ットフォームとコンパイラによって異なります。一部のコンパイラは、コードセグメントに直接格納します。静的変数は常に現在の翻訳単位にのみアクセスでき、名前はエクスポートされないため、名前の衝突が発生することはありません。


5

コンパイル単位で宣言されたデータは、そのファイルの.BSSまたは.Dataに出力されます。BSSで初期化されたデータ、DATAで初期化されていない。

静的データとグローバルデータの違いは、ファイルにシンボル情報が含まれていることです。コンパイラーはシンボル情報を含む傾向がありますが、グローバル情報のみをマークします。

リンカはこの情報を尊重します。静的変数のシンボル情報は破棄またはマングルされるため、静的変数は引き続き(デバッグまたはシンボルオプションを使用して)何らかの方法で参照できます。どちらの場合も、リンカがローカル参照を最初に解決するため、コンパイル単位は影響を受けません。


3

私はobjdumpとgdbでそれを試しました、これが私が得た結果です:

(gdb) disas fooTest
Dump of assembler code for function fooTest:
   0x000000000040052d <+0>: push   %rbp
   0x000000000040052e <+1>: mov    %rsp,%rbp
   0x0000000000400531 <+4>: mov    0x200b09(%rip),%eax        # 0x601040 <foo>
   0x0000000000400537 <+10>:    add    $0x1,%eax
   0x000000000040053a <+13>:    mov    %eax,0x200b00(%rip)        # 0x601040 <foo>
   0x0000000000400540 <+19>:    mov    0x200afe(%rip),%eax        # 0x601044 <bar.2180>
   0x0000000000400546 <+25>:    add    $0x1,%eax
   0x0000000000400549 <+28>:    mov    %eax,0x200af5(%rip)        # 0x601044 <bar.2180>
   0x000000000040054f <+34>:    mov    0x200aef(%rip),%edx        # 0x601044 <bar.2180>
   0x0000000000400555 <+40>:    mov    0x200ae5(%rip),%eax        # 0x601040 <foo>
   0x000000000040055b <+46>:    mov    %eax,%esi
   0x000000000040055d <+48>:    mov    $0x400654,%edi
   0x0000000000400562 <+53>:    mov    $0x0,%eax
   0x0000000000400567 <+58>:    callq  0x400410 <printf@plt>
   0x000000000040056c <+63>:    pop    %rbp
   0x000000000040056d <+64>:    retq   
End of assembler dump.

(gdb) disas barTest
Dump of assembler code for function barTest:
   0x000000000040056e <+0>: push   %rbp
   0x000000000040056f <+1>: mov    %rsp,%rbp
   0x0000000000400572 <+4>: mov    0x200ad0(%rip),%eax        # 0x601048 <foo>
   0x0000000000400578 <+10>:    add    $0x1,%eax
   0x000000000040057b <+13>:    mov    %eax,0x200ac7(%rip)        # 0x601048 <foo>
   0x0000000000400581 <+19>:    mov    0x200ac5(%rip),%eax        # 0x60104c <bar.2180>
   0x0000000000400587 <+25>:    add    $0x1,%eax
   0x000000000040058a <+28>:    mov    %eax,0x200abc(%rip)        # 0x60104c <bar.2180>
   0x0000000000400590 <+34>:    mov    0x200ab6(%rip),%edx        # 0x60104c <bar.2180>
   0x0000000000400596 <+40>:    mov    0x200aac(%rip),%eax        # 0x601048 <foo>
   0x000000000040059c <+46>:    mov    %eax,%esi
   0x000000000040059e <+48>:    mov    $0x40065c,%edi
   0x00000000004005a3 <+53>:    mov    $0x0,%eax
   0x00000000004005a8 <+58>:    callq  0x400410 <printf@plt>
   0x00000000004005ad <+63>:    pop    %rbp
   0x00000000004005ae <+64>:    retq   
End of assembler dump.

ここにobjdumpの結果があります

Disassembly of section .data:

0000000000601030 <__data_start>:
    ...

0000000000601038 <__dso_handle>:
    ...

0000000000601040 <foo>:
  601040:   01 00                   add    %eax,(%rax)
    ...

0000000000601044 <bar.2180>:
  601044:   02 00                   add    (%rax),%al
    ...

0000000000601048 <foo>:
  601048:   0a 00                   or     (%rax),%al
    ...

000000000060104c <bar.2180>:
  60104c:   14 00                   adc    $0x0,%al

つまり、4つの変数はデータセクションイベントに同じ名前で配置されていますが、オフセットは異なります。


それだけではありません。既存の答えでさえ完全ではありません。他のことを言うと、スレッドローカル。
Adriano Repetti 2014年

2

前述のように、データセグメントまたはコードセグメントに格納された静的変数。
スタックまたはヒープに割り当てられないことを確認できます。キーワードは変数のスコープをファイルまたは関数に定義するため、
衝突のリスクはありません。衝突のstatic場合、警告するコンパイラー/リンカーがあります。
良い



1

答えはコンパイラに大きく依存する可能性があるため、おそらく質問を編集する必要があります(つまり、セグメントの概念でさえ、ISO CでもISO C ++でも義務付けられていません)。たとえば、Windowsでは、実行可能ファイルにはシンボル名がありません。1つの 'foo'はオフセット0x100で、もう1つはおそらく0x2B0であり、両方の変換ユニットからのコードは、「それら」のfooのオフセットを知ってコンパイルされます。


0

どちらも個別に保存されますが、他の開発者に明確にしたい場合は、名前空間にまとめることができます。


-1

初期化されていないデータセグメントまたは初期化されたデータセグメントとも呼ばれるbss(シンボルで始まるブロック)に格納されていることはすでにご存じでしょう。

簡単な例を見てみましょう

void main(void)
{
static int i;
}

上記の静的変数は初期化されていないため、初期化されていないデータセグメント(bss)に移動します。

void main(void)
{
static int i=10;
}

もちろん10で初期化されるため、初期化されたデータセグメントに移動します。

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