203

実行可能ファイルのどのセグメント (.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 ツールチェーンを使用すると仮定します。

4

16 に答える 16

145

static がどこに行くかは、それらがzero-initializedであるかどうかによって異なります。ゼロで初期化された静的データは.BSS (Block Started by Symbol)に入り、ゼロで初期化されていないデータは.DATAに入ります

于 2008-09-18T15:07:35.283 に答える
125

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

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

この概念を説明する図を次に示します。

ここに画像の説明を入力

これらの概念を説明する非常に良いリンクは次のとおりです。 C のメモリ管理: ヒープとスタック

于 2012-07-28T05:08:36.263 に答える
35

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

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 領域のいずれかになければなりません (明示的に初期化されているかどうかによって異なります)。その後、すべての関数またはソース ファイル内の関数のいずれかにスコープが設定されます。

于 2008-09-20T19:38:30.473 に答える
23

データの保存場所は実装によって異なります。

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

于 2008-09-18T14:33:38.453 に答える
15

「グローバルおよび静的」エリアで:)

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

  • ヒープ
  • フリーストア
  • スタック
  • グローバルと静的
  • 定数

あなたの質問に対する詳細な回答については、こちらを参照してください。

以下は、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.
于 2008-09-18T14:43:35.310 に答える
14

自分で見つける方法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
于 2015-06-04T10:59:44.690 に答える
13

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

関数内でのstatic の使用は異なります。変数は関数からのみ可視であり (静的であるかどうかに関係なく)、その値はその関数の呼び出し間で保持されます。

事実上、static はその場所に応じて 2 つの異なることを行います。ただし、どちらの場合も、変数の可視性は、リンク時に名前空間の衝突を簡単に防ぐことができるように制限されています。

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

于 2008-09-18T14:35:36.310 に答える
10

これは(理解しやすい)方法です:

スタック、ヒープ、静的データ

于 2018-03-12T11:24:52.410 に答える
6

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

于 2008-09-18T14:33:42.010 に答える
4

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

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

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

于 2008-09-18T20:27:31.567 に答える
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 つの変数はデータ セクション イベントに同じ名前で配置されていますが、オフセットが異なります。

于 2014-10-13T17:43:41.033 に答える
2

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

于 2008-09-18T15:00:20.620 に答える
1

この質問は少し古すぎますが、有用な情報を誰も指摘していないため、シンボルテーブルに同じ名前の静的変数が格納されていることを説明する「mohit12379」による投稿を確認してください:http: //www.geekinterview.com/question_details/ 24745

于 2011-03-20T21:12:06.387 に答える
1

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

于 2008-09-18T14:34:37.827 に答える
0

they're both going to be stored independently, however if you want to make it clear to other developers you might want to wrap them up in namespaces.

于 2008-09-18T14:38:53.323 に答える