2

/usr/include/c++/4.6/bits/ios_base.h の std::ios::app の定義を調べたところ、std::ios::app が const 静的変数として定義されていることがわかりました。

typedef _Ios_Openmode openmode;

/// Seek to end before each write.
static const openmode app =     _S_app;

_Ios_Openmode は、同じヘッダー ファイルで定義されています。

enum _Ios_Openmode 
{ 
  _S_app        = 1L << 0,
  _S_ate        = 1L << 1,
  _S_bin        = 1L << 2,
  _S_in         = 1L << 3,
  _S_out        = 1L << 4,
  _S_trunc      = 1L << 5,
  _S_ios_openmode_end = 1L << 16 
};

静的変数には内部リンケージがあり、すべての翻訳単位にはこの静的変数の独自のコピーがあることはよく知られています。つまり、異なる翻訳単位の静的変数には異なるアドレスが必要です。ただし、std::ios::app のアドレスを出力するために 2 つの別々のプログラムを使用したところ、出力されたアドレスが同じであることがわかりました。

ソースファイル test1.cpp

#include <iostream>

int main() {
    std::cout << "address: " << &std::ios::app << std::endl;
    return 0;
}

結果

address: 0x804a0cc

ソース ファイル test2.cpp は test1.cpp と同じであり、結果は同じです。

address: 0x804a0cc

これは本当に私を混乱させました.異なる翻訳単位の静的変数は異なるアドレスを持つべきではありませんか?


更新: コメントで指摘されているように、std::ios::app は静的 const 変数ではなく静的データ メンバーです。静的データ メンバには外部リンケージがあり、異なる翻訳単位の静的データ メンバのアドレスは同じである必要があります。2 番目のポイントは、この事実を検証するための私の方法が間違っているということです。異なるプログラムが異なる翻訳単位を意味するわけではありません。

4

2 に答える 2

4

test1.cppとは 2 つの別個test2.cppの翻訳単位であるだけでなく、それらを 2 つのまったく異なるプログラムにコンパイルし、別個のプロセスとして実行します。各プロセスのメモリ空間は、実行するたびにオペレーティング システムによって新たに定義され、各プロセスのアドレスの絶対値を比較することはできません。これらのアドレスは、各プロセスの仮想アドレス空間に対して相対的に解釈されるため、プロセスを並行して実行する場合でも、それらは同一である可能性があります。(*)

内部リンケージの効果を確認したい場合は、コンパイル後に 2 つの翻訳単位をリンクする必要があります。

これは次の方法で行うことができます。

ヘッダーを定義しtest.hます:

const static int i = 0;

で関数の実装を定義しprint_i_1ますtest1.cpp

#include <iostream>
#include "test.h"

void print_i_1()
{ std::cout << &i << std::endl; }

で関数の実装を定義しprint_i_2ますtest2.cpp

#include <iostream>
#include "test.h"

void print_i_2()
{ std::cout << &i << std::endl; }

どちらの関数も同じ操作を実行しますが、別々にコンパイルされている限り、それぞれが の異なるインスタンスを参照することに注意してくださいi

また、これらのプログラムには の定義が含まれていないことにも注意してくださいmain()。これを 3 番目のファイルで提供しますtest.cpp

extern void print_i_1();
extern void print_i_2();

int main()
{
  print_i_1();
  print_i_2();

  return 0;
}

次に、各 .cpp ファイルをコンパイルします (したがって、3 つの翻訳単位があります)。私は GCC を使用していますが、他のコンパイラでも同様のことが可能です。

g++ -W -Wall -g -o test1.o -c ./test1.cpp
g++ -W -Wall -g -o test2.o -c ./test2.cpp
g++ -W -Wall -g -o test.o -c ./test.cpp

そして、それらをリンクします。

g++ -W -Wall -g -o test ./test.o ./test1.o ./test2.o

結果の実行可能ファイルを実行したときに得られる出力testは次のとおりです。

0x4009c8
0x4009d0

2 つの異なるアドレス。

C++ では、名前空間スコープ (グローバル名前空間スコープを含む) の変数に対してこれを行うために、キーワードstaticは実際には必要ないことに注意してください。const明示的に宣言されていない限り、それらは自動的に内部リンケージを持ちますextern


(*)結局のところ、標準ライブラリで定義されているクラスの静的メンバーのアドレスを使用しているようです。その場合、次の 2 つの注意事項があります。

  • 標準ライブラリに動的にリンクすると、オブジェクトは実際には 2 つの別個のプロセス間でも共有できます (ただし、各プロセスには独自のアドレス空間があるため、表示されるアドレスが必ずしも同じになるとは限りません)。
  • ただし、静的クラス メンバーには外部リンケージがあるため、この場合、最初から想定が間違っていたはずです。
于 2013-07-25T03:19:01.600 に答える
1

9.4.2 では、静的データ メンバーの規則が定義されています。

9.4.2/3 は、静的 const リテラルが「ブレースまたはイコール初期化子を指定できる」と定義しています。つまり、以下の X::x のように定義できます。

9.4.2/4 では、1 つの定義のみが存在できると定義しています (「1 つの定義規則」(odr)、3.2 を参照)。

最後に、9.4.2/5 では、名前空間スコープ内のクラスのすべての静的データ メンバーが外部リンケージを持つことがさらに定義されています。

例:

// test.h
struct X {
    static const int x = 10;
};

// main.cpp

#include <iostream>
#include "test.h"
void f();
int main(int argc, const char* argv[]) {
    std::cout << &X::x << std::endl;
    f();
    return 0;
}

// test.cpp
#include <iostream>
#include "test.h"

void f() {
    std::cout << &X::x << std::endl;
}

出力:

001A31C4
001A31C4
于 2013-07-25T02:52:03.190 に答える