93

一部をリファクタリングする#definesと、C++ ヘッダー ファイルで次のような宣言に出くわしました。

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

問題は、静電気によってどのような違いが生じるかということです。#ifndef HEADER #define HEADER #endif古典的なトリックのため、ヘッダーを複数含めることはできないことに注意してください(それが重要な場合)。

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

4

12 に答える 12

112

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

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

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

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

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

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

C仕様のドラフトから:

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

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

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

于 2008-09-18T15:37:28.627 に答える
109

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

staticグローバル レベルの変数は、インクルード経由で取得したか、メイン ファイルにあったかに関係なく、独自のソース ファイルでのみ表示されます。


編集者注: C++ では、宣言に or キーワードのないオブジェクトは暗黙的にconstです。staticexternstatic

于 2008-09-18T13:43:40.133 に答える
49

静的とは、ファイルごとに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

于 2008-09-18T13:50:21.817 に答える
6

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

ああ

const int i = 10;

one.cpp

#include "a.h"

func()
{
   cout << i;
}

2.cpp

#include "a.h"

func1()
{
   cout << i;
}

これが C プログラムの場合、i(外部リンケージによる) の「複数定義」エラーが発生します。

于 2010-04-27T02:18:06.607 に答える
5

このレベルのコードでの static 宣言は、変数が現在のコンパイル単位でのみ可視であることを意味します。これは、そのモジュール内のコードのみがその変数を参照することを意味します。

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

この特定のケースでは、「static」キーワードは何の利点も提供していないようです。私は何かが欠けているかもしれませんが、それは問題ではないようです.

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

于 2008-09-18T13:46:49.127 に答える
2

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

于 2008-09-18T13:56:22.520 に答える
2

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

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

この場合、C と C++ の標準によって違いが生じます。

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

ヘッダーが異なるファイルに含まれている場合 (同じグローバル名が 2 回定義されている場合)、最新のリンカーは ANOTHER_VAL について文句を言う可能性があることに注意してください。ANOTHER_VAL が別のファイルで異なる値に初期化されている場合は間違いなく文句を言うでしょう。

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

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

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

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

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

staticを省略すると、リンケージはデフォルトでexternになります。繰り返しになりますが、constネスによって救われました - コンパイラは使用を最適化/インライン化できます。constを削除すると、リンク時に複数定義されたシンボルエラーが発生します。

于 2008-09-18T13:49:53.830 に答える
1

const変数は C++ ではデフォルトで静的ですが、extern C です。したがって、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
于 2015-01-23T09:35:56.273 に答える
0

Static は、別のコンパイル単位がその変数を外部化するのを防ぎます。これにより、コンパイラーは、変数の値が使用されている場所に単に「インライン化」でき、変数のメモリ ストレージを作成しません。

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

于 2008-09-18T13:35:38.623 に答える
-2

Static は、コンパイラが複数のインスタンスを追加するのを防ぎます。これは #ifndef 保護ではそれほど重要ではなくなりますが、ヘッダーが 2 つの別個のライブラリに含まれ、アプリケーションがリンクされていると仮定すると、2 つのインスタンスが含まれます。

于 2008-09-18T13:40:11.280 に答える