Cのキーワード「静的」には、2つの根本的に異なる意味があります。
スコープの制限
このコンテキストでは、「静的」と「extern」を組み合わせて、変数または関数名のスコープを制御します。Staticを使用すると、変数または関数の名前を単一のコンパイル単位内でのみ使用でき、コンパイル単位テキスト内の宣言/定義の後に存在するコードでのみ使用できます。
この制限自体は、プロジェクトに複数のコンパイルユニットがある場合にのみ意味を持ちます。コンパイルユニットが1つしかない場合でも、それは機能しますが、それらの効果はほとんど無意味です(コンパイラーが生成したものを読み取るためにオブジェクトファイルを掘り下げるのが好きでない限り)。
前述のように、このコンテキストのこのキーワードは、他のコンパイルユニットで見つかった同じ名前で変数または関数名をリンク可能にすることによって、反対のことを行うキーワード 'extern'とペアになります。したがって、「静的」は、現在のコンパイル単位内で変数または名前を見つける必要があると見なすことができますが、「外部」は、クロスコンパイル単位のリンケージを許可します。
静的寿命
静的存続期間とは、変数がプログラムの存続期間全体に渡って存在することを意味します(ただし、どれだけ長いかを問いません)。「静的」を使用して関数内で変数を宣言/定義する場合、それは、最初の使用の前に変数が作成されることを意味します(つまり、私が経験するたびに、変数はmain()が開始する前に作成され、その後は破棄されません。関数の実行が完了し、呼び出し元に戻る場合でもそうではありません。また、関数の外部で宣言された静的ライフタイム変数と同じように、それらは同時に(main()が開始する前に)セマンティックゼロ(初期化が提供されていない場合)または指定された場合は指定された明示的な値に初期化されます。
これは、「auto」タイプの関数変数とは異なります。これは、関数に入るたびに新しく(または、as-if newの場合)作成され、関数が終了すると破棄されます(または、破棄された場合)。
関数の範囲に直接影響する、関数の外部の変数定義に「静的」を適用する影響とは異なり、「関数本体」内で関数変数を宣言すると、「静的」はスコープに影響を与えません。スコープは、関数本体内で定義されたという事実によって決定されます。関数内で定義された静的ライフタイム変数は、関数本体内で定義された他の「自動」変数と同じスコープを持ちます-関数スコープ。
概要
したがって、「静的」キーワードは、「非常に異なる意味」に相当するさまざまなコンテキストを持っています。このように2つの方法で使用された理由は、別のキーワードの使用を避けるためでした。(それについては長い議論がありました。)プログラマーはその使用を容認できると感じられ、言語の他のキーワードを回避することの価値は(他の議論よりも)より重要でした。
(関数の外部で宣言されたすべての変数は静的な有効期間を持ち、それを実現するためにキーワード「静的」を必要としません。したがって、この種類のキーワードを解放してそこでまったく異なるものを意味するために使用します:「1回のコンパイルでのみ可視ユニット。それは一種のハックです。)
特定の注記
static volatile unsigned char PORTB @ 0x06;
ここで「静的」という言葉は、リンカが複数のコンパイルユニットで見つかる可能性のあるPORTBの複数の出現を一致させようとしないことを意味すると解釈されます(コードが複数あると仮定します)。
PORTBの「場所」(または通常はアドレスであるラベルの数値)を指定するために、特別な(移植できない)構文を使用します。したがって、リンカにはアドレスが与えられ、そのためにアドレスを見つける必要はありません。この行を使用するコンパイルユニットが2つある場合、いずれにしても、それぞれが同じ場所を指し示すことになります。したがって、ここに「外部」というラベルを付ける必要はありません。
彼らが「外部」を使用した場合、問題が発生する可能性があります。その後、リンカは、複数のコンパイルで見つかったPORTBへの複数の参照を確認できます(照合を試みます)。それらのすべてがこのようなアドレスを指定し、アドレスが何らかの理由で同じではない[間違い?]場合、何をすべきですか?文句を言う?それとも?(技術的には、「のextern」で経験則のみということになるONEコンパイル単位が値を指定するであろうし、他のものはいけません。)
それを「静的」としてラベル付けする方が簡単であり、リンカーが競合を心配することを回避し、アドレスを不必要なものに変更した人のミスマッチアドレスのミスのせいにするだけです。
どちらの場合も、変数は「静的存続期間」を持つものとして扱われます。(そして「揮発性」。)
宣言はない定義が、すべての定義が宣言されています
Cでは、定義によってオブジェクトが作成されます。また、それを宣言します。しかし、宣言は通常(以下の箇条書きを参照)オブジェクトを作成しません。
以下は、定義と宣言です。
static int a;
static int a = 7;
extern int b = 5;
extern int f() { return 10; }
以下は定義ではなく、単なる宣言です。
extern int b;
extern int f();
宣言は実際のオブジェクトを作成しないことに注意してください。それらはそれに関する詳細を宣言するだけであり、コンパイラーはこれを使用して、適切なコードを生成し、必要に応じて警告およびエラーメッセージを提供できます。
上記では、「通常」と忠告しています。場合によっては、宣言によってオブジェクトが作成される可能性があるため、リンカーによって(コンパイラーによってではなく)定義に昇格されます。したがって、このまれなケースでも、Cコンパイラーは宣言が単なる宣言であると見なします。宣言の必要なプロモーションを行うのは、リンカーフェーズです。この点に注意してください。
上記の例で、「extern int b;」の宣言しかないと判明した場合は、リンクされたすべてのコンパイル単位で、リンカーは定義を作成する責任を負います。これはリンク時のイベントであることに注意してください。コンパイル中、コンパイラーは完全に認識しません。このタイプの宣言が最もプロモートされる場合、リンク時にのみ決定できます。
コンパイラーは、「静的int a;」を認識しています。リンク時にはリンカによって昇格できないため、これは実際にはコンパイル時の定義です。