web-dev-qa-db-ja.com

ヘッダーファイルの変数宣言-静的かどうか

いくつかの#definesをリファクタリングするときに、C++ヘッダーファイルで次のような宣言に遭遇しました。

static const unsigned int VAL = 42;
const unsigned int ANOTHER_VAL = 37;

問題は、もしあるとすれば、どのような違いがあるのでしょうか?古典的な#ifndef HEADER#define HEADER#endifトリック(重要な場合)のため、ヘッダーを複数含めることはできません。

ヘッダーが複数のソースファイルに含まれている場合、静的はVALのコピーが1つだけ作成されることを意味しますか?

86
Rob

staticは、含まれるソースファイルごとにVALのコピーが1つ作成されることを意味します。ただし、複数のインクルードがVALの複数の定義にならないことも意味します。リンク時に衝突します。 Cでは、staticなしで、1つのソースファイルのみがVALを定義し、他のソースファイルがexternを宣言したことを確認する必要があります。通常は、ソースファイルで(おそらく初期化子を使用して)定義し、ヘッダーファイルにextern宣言を配置することでこれを行います。

グローバルレベルのstatic変数は、インクルードを介してそこに到達したか、メインファイルに存在するかに関係なく、独自のソースファイルでのみ表示されます。


編集者注: C++では、conststaticキーワードも宣言にないexternオブジェクトは、暗黙的にstaticです。

102
Justsalt

ファイルスコープ変数のstaticおよびexternタグは、他の翻訳単位(つまり、他の.cまたは.cppファイル)でアクセスできるかどうかを決定します。

  • staticは、変数内部リンケージを提供し、他の翻訳単位から隠します。ただし、内部リンケージを持つ変数は、複数の翻訳単位で定義できます。

  • externは変数に外部リンケージを提供し、他の翻訳単位から見えるようにします。通常、これは、変数を1つの翻訳単位でのみ定義する必要があることを意味します。

デフォルト(staticまたはexternを指定しない場合)は、CとC++が異なる領域の1つです。

  • Cでは、ファイルスコープの変数はデフォルトでextern(外部リンケージ)です。 Cを使用している場合、VALstaticで、ANOTHER_VALexternです。

  • C++では、ファイルスコープの変数は、staticである場合はデフォルトでconst(内部リンケージ)であり、そうでない場合はデフォルトでexternです。 C++を使用している場合、VALANOTHER_VALは両方ともstaticです。

C仕様 のドラフトから:

6.2.2識別子のリンク... -5-関数の識別子の宣言にストレージクラス指定子がない場合、そのリンケージは、ストレージクラス指定子externで宣言された場合とまったく同じように決定されます。オブジェクトの識別子の宣言にファイルスコープがあり、ストレージクラス指定子がない場合、そのリンケージは外部です。

C++仕様 のドラフトから:

7.1.1-ストレージクラス指定子[dcl.stc] ... -6- storage-class-specifierのないネームスペーススコープで宣言された名前は、前の宣言のために内部リンケージがない限り、外部リンケージを持ちます。宣言されたconst constとして宣言され、externとして明示的に宣言されていないオブジェクトには、内部リンケージがあります。

107
bk1e

静的とは、ファイルごとに1つのコピーを取得することを意味しますが、他の人とは異なり、そうすることは完全に合法です。これは、小さなコードサンプルで簡単にテストできます。

test.h:

static int TEST = 0;
void test();

test1.cpp:

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

int main(void) {
    std::cout << &TEST << std::endl;
    test();
}

test2.cpp:

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

void test() {
    std::cout << &TEST << std::endl;
}

これを実行すると、次の出力が得られます。

0x446020
0x446040

46
slicedlime

C++のconst変数には内部リンケージがあります。したがって、staticを使用しても効果はありません。

a.h

const int i = 10;

one.cpp

#include "a.h"

func()
{
   cout << i;
}

two.cpp

#include "a.h"

func1()
{
   cout << i;
}

これがCプログラムである場合、iの '複数定義'エラーが発生します(外部リンクのため)。

6
Nitin

このレベルのコードでの静的宣言は、変数が現在のコンパイル単位でのみ表示されることを意味します。これは、そのモジュール内のコードのみがその変数を見るということです。

変数を静的に宣言するヘッダーファイルがあり、そのヘッダーが複数のC/CPPファイルに含まれている場合、その変数はそれらのモジュールに対して「ローカル」になります。ヘッダーが含まれるN個の場所について、その変数のN個のコピーがあります。それらは互いにまったく関係していません。これらのソースファイル内のコードは、そのモジュール内で宣言されている変数のみを参照します。

この特定のケースでは、「静的」キーワードは利点を提供していないようです。私は何かを見逃しているかもしれませんが、それは問題ではないようです-私は以前にこのようなことをしたことを見たことがありません。

インライン化に関しては、この場合、変数はインライン化される可能性がありますが、それはconstが宣言されているためです。コンパイラmightはモジュールの静的変数をインライン化する可能性が高くなりますが、それは状況とコンパイルされるコードに依存します。コンパイラが「静的」をインライン化する保証はありません。

5
Mark

質問に答えるには、「ヘッダーが複数のソースファイルに含まれている場合、静的はVALのコピーが1つだけ作成されることを意味しますか?」...

[〜#〜] no [〜#〜]。 VALは、ヘッダーを含むすべてのファイルで常に個別に定義されます。

CとC++の標準は、この場合に違いを引き起こします。

Cでは、ファイルスコープの変数はデフォルトでexternです。 Cを使用している場合、VALは静的で、ANOTHER_VALはexternです。

モダンリンカーは、ヘッダーが異なるファイルに含まれている場合(同じグローバル名が2回定義されている場合)、ANOTHER_VALについて文句を言う可能性があります。

C++では、ファイルスコープの変数は、constの場合はデフォルトで静的であり、そうでない場合はデフォルトでexternです。 C++を使用している場合、VALとANOTHER_VALは両方とも静的です。

また、両方の変数がconstに指定されているという事実も考慮する必要があります。理想的には、コンパイラは常にこれらの変数をインライン化することを選択し、それらのストレージを含めないようにします。ストレージを割り当てることができる理由は、ホスト全体にあります。私が考えることができるもの...

  • デバッグオプション
  • ファイルで取得されたアドレス
  • コンパイラは常にストレージを割り当てます(複雑なconst型は簡単にインライン化できないため、基本型の特殊なケースになります)
2
itj

Cの本(無料オンライン)には、リンケージに関する章があり、「静的」の意味をより詳細に説明しています(ただし、正しい答えは他のコメントで既に与えられています): http://publications.gbdirect.co .uk/c_book/chapter4/linkage.html

2
Jan de Vos

静的変数も定義せずに宣言することはできません(これは、ストレージクラス修飾子staticとexternが相互に排他的であるためです)。静的変数はヘッダーファイルで定義できますが、これにより、ヘッダーファイルを含む各ソースファイルが変数の独自のプライベートコピーを持つことになりますが、これはおそらく意図したものではありません。

2
Gajendra Kumar

これらの宣言がグローバルスコープにある(つまり、メンバー変数ではない)と仮定すると、

staticは「内部リンケージ」を意味します。この場合、constと宣言されているため、これはコンパイラーによって最適化/インライン化できます。 constを省略すると、コンパイラーは各コンパイル単位にストレージを割り当てる必要があります。

staticを省略すると、デフォルトでリンケージはexternになります。繰り返しますが、const nessによって保存されています-コンパイラーは、最適化/インライン使用が可能です。 constをドロップすると、リンク時にmultiply defined symbolsエラーが発生します。

1
Seb Rose

const変数はC++ではデフォルトで静的ですが、Cはexternです。したがって、C++を使用する場合、これはどのような構造を使用するのか意味がありません。

(7.11.6 C++ 2003、およびApexndix Cにはサンプルがあります)

CおよびC++プログラムとしてソースをコンパイル/リンク比較する例:

bruziuz:~/test$ cat a.c
const int b = 22;
int main(){return 0;}
bruziuz:~/test$ cat b.c
const int b=2;
bruziuz:~/test$ gcc -x c -std=c89 a.c b.c
/tmp/ccSKKIRZ.o:(.rodata+0x0): multiple definition of `b'
/tmp/ccDSd0V3.o:(.rodata+0x0): first defined here
collect2: error: ld returned 1 exit status
bruziuz:~/test$ gcc -x c++ -std=c++03 a.c b.c 
bruziuz:~/test$ 
bruziuz:~/test$ gcc --version | head -n1
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.5) 5.4.0 20160609
1
bruziuz

Staticは、別のコンパイルユニットがその変数を外部に格納することを防ぎ、コンパイラが変数の値を使用する場所で「インライン化」し、そのためのメモリストレージを作成しないようにします。

2番目の例では、コンパイラは他のソースファイルが外部にないことを想定できないため、実際にその値をメモリのどこかに格納する必要があります。

0
Jim Buck