回答:
CとC ++の両方で使用されます。
ご想像のとおり、static
パーツのスコープはそのコンパイルユニットに限定されます。また、静的な初期化も提供します。const
誰かに変更させないようコンパイラーに指示するだけです。この変数は、アーキテクチャに応じてデータまたはbssセグメントに配置され、読み取り専用とマークされたメモリ内にある可能性があります。
以上が、Cがこれらの変数を処理する方法(またはC ++が名前空間変数を処理する方法)です。C ++では、マークstatic
されたメンバーは、特定のクラスのすべてのインスタンスで共有されます。プライベートかどうかは、1つの変数が複数のインスタンスで共有されるという事実に影響を与えません。持つconst
任意のコードがそれを修正しようとする場合があるにして警告を表示します。
完全にプライベートの場合、クラスの各インスタンスは独自のバージョンを取得します(オプティマイザにもかかわらず)。
多くの人が基本的な答えを出しましたが、C ++ではconst
デフォルトstatic
でnamespace
レベルにあることを誰も指摘しませんでした(そして一部は間違った情報を与えました)。C ++ 98標準セクション3.5.3を参照してください。
最初にいくつかの背景:
翻訳単位:プリプロセッサがすべてのインクルードファイルを(再帰的に)インクルードした後のソースファイル。
静的リンケージ:シンボルは、その翻訳単位内でのみ使用できます。
外部リンケージ:シンボルは他の翻訳単位から利用できます。
namespace
レベルこれには、グローバル名前空間またはグローバル変数が含まれます。
static const int sci = 0; // sci is explicitly static
const int ci = 1; // ci is implicitly static
extern const int eci = 2; // eci is explicitly extern
extern int ei = 3; // ei is explicitly extern
int i = 4; // i is implicitly extern
static int si = 5; // si is explicitly static
static
関数呼び出し間で値が維持されることを意味します。
関数static
変数のセマンティクスは、プログラムのデータセグメント(スタックやヒープではない)に存在するという点でグローバル変数に似ています。この質問を参照してくださいstatic
。変数のライフタイムの詳細についてを参照してください。
class
レベルstatic
値はクラスのすべてのインスタンス間で共有され、const
変更されないことを意味します。
const int *foo(int x) {const int b=x;return &b};
対const int *foo(int x) {static const int b=x;return &b};
const
ものstatic
であるため、後者についてのみ意味することについてのメモを含める必要があります。
const
宣言static
もそこにあるという意味ですか?のように、キャストしconst
て値を変更すると、すべての値が変更されますか?
const
は関数レベルで静的であることを意味しません。これは同時実行の悪夢(const!=定数式)であり、関数レベルでのすべては暗黙的auto
です。この質問にも[c]というタグが付いているので、グローバルレベルconst int
は暗黙的extern
にC であることに言及する必要があります。ただし、ここでのルールはC ++を完全に記述しています。
static
で、変数が静的期間(プログラムの最初から最後まで続くコピーが1つだけ存在する)であり、特に指定されていない場合は内部/静的リンクがあることを示します(これは関数のオーバーライドされます)ローカル静的変数のリンケージ、または静的メンバーのクラスのリンケージ)。主な違いは、static
が有効な各状況でこれが意味することです。
そのコード行は実際にはいくつかの異なるコンテキストで表示され、ほぼ同じように動作しますが、小さな違いがあります。
// foo.h
static const int i = 0;
' i
'は、ヘッダーを含むすべての翻訳単位で表示されます。ただし、実際にオブジェクトのアドレス(たとえば&i
、' ')を使用しない限り、コンパイラーは ' i
'を単純にタイプセーフとして扱うと確信しています0
。さらに2つの変換単位が ' &i
'をとる場合、アドレスは変換単位ごとに異なります。
// foo.cc
static const int i = 0;
' i
'には内部リンケージがあるため、この翻訳単位の外部から参照することはできません。ただし、そのアドレスを使用しない限り、ほとんどの場合、タイプセーフとして扱われます0
。
指摘する価値のあることの1つは、次の宣言です。
const int i1 = 0;
で正確に同じstatic const int i = 0
。で宣言されconst
、明示的に宣言されていない名前空間の変数は、extern
暗黙的に静的です。これについて考えると、ODRの破損を避けるためにキーワードをconst
常に必要とせずに、ヘッダーファイルで変数を宣言できるようにすることがC ++委員会の意図でしたstatic
。
class A {
public:
static const int i = 0;
};
上記の例ではi
、アドレスが不要な場合は、 ' 'を定義する必要がないことを標準で明示的に指定しています。言い換えるi
と、タイプセーフ0として' ' のみを使用する場合、コンパイラーはそれを定義しません。クラスバージョンと名前空間バージョンの1つの違いは、 ' i
'(2つ以上の変換ユニットで使用される場合)のアドレスがクラスメンバーで同じになることです。アドレスを使用する場合、その定義が必要です。
// a.h
class A {
public:
static const int i = 0;
};
// a.cc
#include "a.h"
const int A::i; // Definition so that we can take the address
これは小さなスペースの最適化です。
あなたが言う時
const int foo = 42;
定数を定義するのではなく、読み取り専用変数を作成します。コンパイラーは、fooを検出するたびに42を使用するのに十分なほどスマートですが、初期化されたデータ領域にスペースを割り当てます。これは、定義されているように、fooには外部リンケージがあるためです。別のコンパイル単位は言うことができます:
extern const int foo;
その価値にアクセスするため。そのコンパイル単位はfooの値が何であるかを知らないので、それは良い習慣ではありません。それはそれがconst intであることを知っているだけで、使用されるたびにメモリから値をリロードする必要があります。
今、それが静的であることを宣言することによって:
static const int foo = 42;
コンパイラーは通常の最適化を行うことができますが、「このコンパイルユニットの外部の誰もfooを見ることができず、常に42であるため、スペースを割り当てる必要がないこともわかります」とも言えます。
C ++では、名前が現在のコンパイル単位をエスケープしないようにするための推奨される方法は、匿名の名前空間を使用することです。
namespace {
const int foo = 42; // same as static definition above
}
「int」がありません。そのはず:
const static int foo = 42;
CおよびC ++では、ローカルファイルスコープの値が42の整数定数を宣言します。
なぜ42?あなたがまだ知らない場合(そしてあなたが知らないと信じがたい場合)、それはAnswer to Life、Universe、およびEverythingへの参照です。
すべてのすばらしい答えに、私は少し詳細を追加したいと思います:
プラグイン(たとえば、CADシステムによってロードされるDLLまたは.soライブラリ)を作成する場合、staticは次のような名前の衝突を回避する救命ツールです。
さらに悪いことに:ステップ3は、コンパイラーの最適化、プラグインのロードメカニズムなどによって異なる動作をする場合があります。
2つのプラグインの2つのヘルパー関数(同じ名前、異なる動作)でこの問題が一度発生しました。それらを静的と宣言することで問題は解決しました。
C99 / GNU99仕様によると:
static
ストレージクラス指定子です
デフォルトではファイルレベルのスコープのオブジェクトには外部リンケージがあります
const
タイプ修飾子です(タイプの一部です)
すぐ左のインスタンスに適用されるキーワード-つまり
MyObj const * myVar;
-const修飾オブジェクトタイプへの非修飾ポインタ
MyObj * const myVar;
-非修飾オブジェクトタイプへのconst修飾ポインタ
左端の使用法-変数ではなくオブジェクトタイプに適用されます
const MyObj * myVar;
-const修飾オブジェクトタイプへの非修飾ポインタTHUS:
static NSString * const myVar;
-内部リンケージを持つ不変文字列への定数ポインター。
不在static
キーワードは、変数名がグローバルになりますと、アプリケーション内で名前の競合を引き起こす可能性があります。
C ++ 17 inline
変数
「C ++ const static」をグーグル化した場合、これは本当にC ++ 17インライン変数を使用したい可能性が非常に高いです。
この素晴らしいC ++ 17機能により、次のことが可能になります。
constexpr
:constexpr externを宣言する方法は?main.cpp
#include <cassert>
#include "notmain.hpp"
int main() {
// Both files see the same memory address.
assert(¬main_i == notmain_func());
assert(notmain_i == 42);
}
notmain.hpp
#ifndef NOTMAIN_HPP
#define NOTMAIN_HPP
inline constexpr int notmain_i = 42;
const int* notmain_func();
#endif
notmain.cpp
#include "notmain.hpp"
const int* notmain_func() {
return ¬main_i;
}
コンパイルして実行:
g++ -c -o notmain.o -std=c++17 -Wall -Wextra -pedantic notmain.cpp
g++ -c -o main.o -std=c++17 -Wall -Wextra -pedantic main.cpp
g++ -o main -std=c++17 -Wall -Wextra -pedantic main.o notmain.o
./main
インライン変数のC ++標準
C ++標準では、アドレスが同じであることを保証しています。C ++ 17 N4659標準ドラフト 10.1.6「インライン指定子」:
6外部リンケージのあるインライン関数または変数は、すべての変換ユニットで同じアドレスを持つ必要があります。
cppreference https://en.cppreference.com/w/cpp/language/inlineは、static
指定されていない場合、外部リンクがあることを説明しています。
GCCインライン変数の実装
それがどのように実装されているかを観察できます:
nm main.o notmain.o
を含む:
main.o:
U _GLOBAL_OFFSET_TABLE_
U _Z12notmain_funcv
0000000000000028 r _ZZ4mainE19__PRETTY_FUNCTION__
U __assert_fail
0000000000000000 T main
0000000000000000 u notmain_i
notmain.o:
0000000000000000 T _Z12notmain_funcv
0000000000000000 u notmain_i
とman nm
言うu
:
"u"シンボルはユニークなグローバルシンボルです。これは、ELFシンボルバインディングの標準セットに対するGNU拡張です。このようなシンボルの場合、ダイナミックリンカーは、プロセス全体でこの名前とタイプのシンボルが1つだけ使用されていることを確認します。
そのため、専用のELF拡張があることがわかります。
C ++ 17より前: extern const
C ++ 17以前、およびCでは、を使用して非常に類似した効果を実現できますextern const
。これにより、単一のメモリロケーションが使用されます。
上の欠点inline
は次のとおりです。
constexpr
この手法で変数を作成することはできません。それをinline
許可するのは次のとおりです。constexpr externを宣言する方法は?main.cpp
#include <cassert>
#include "notmain.hpp"
int main() {
// Both files see the same memory address.
assert(¬main_i == notmain_func());
assert(notmain_i == 42);
}
notmain.cpp
#include "notmain.hpp"
const int notmain_i = 42;
const int* notmain_func() {
return ¬main_i;
}
notmain.hpp
#ifndef NOTMAIN_HPP
#define NOTMAIN_HPP
extern const int notmain_i;
const int* notmain_func();
#endif
C ++ 17以前のヘッダーのみの代替
これらはextern
ソリューションほど良くありませんが、機能し、メモリの場所を1つしか占有しません。
constexpr
なぜならこの関数は、constexpr
意味inline
とinline
、すべての翻訳単位に表示される定義(力を)ことができます:
constexpr int shared_inline_constexpr() { return 42; }
まともなコンパイラーも呼び出しをインライン化すると思います。
次のように、const
またはconstexpr
静的変数を使用することもできます。
#include <iostream>
struct MyClass {
static constexpr int i = 42;
};
int main() {
std::cout << MyClass::i << std::endl;
// undefined reference to `MyClass::i'
//std::cout << &MyClass::i << std::endl;
}
しかし、そのアドレスを取得するなどのことはできません。そうしないと、アドレスがodrで使用されます。constexpr静的データメンバーの定義も参照してください。
C
Cでは、状況はC ++ 17より前のC ++と同じです。Cで「静的」とはどういう意味ですか?
唯一の違いは、C ++ではグローバルをconst
意味static
しますが、Cでは意味がありません。C++の「静的const」と「const」のセマンティクス
それを完全にインライン化する方法はありますか?
TODO:メモリをまったく使用せずに、変数を完全にインライン化する方法はありますか?
プリプロセッサが行うこととよく似ています。
これはどういうわけか必要になります:
関連:
Ubuntu 18.10、GCC 8.2.0でテスト済み。
これは、コンパイルモジュール(.cppファイル)でのみ表示/アクセス可能なグローバル定数です。この目的で静的を使用するBTWは非推奨です。匿名の名前空間と列挙型を使用することをお勧めします:
namespace
{
enum
{
foo = 42
};
}
enum
この文脈でどのような利益があるのかはっきりしていません。詳しく説明しますか?これenums
は通常、コンパイラーが値にスペースを割り当てないようにする(最近のコンパイラーはこのenum
ハックは必要ない)ため、および値へのポインターの作成を防ぐためにのみ使用されます。
非公開にしても、ヘッダーに表示されます。私は「最も弱い」方法を使用する傾向があります。Scott Meyersによるこの古典的な記事を参照してください:http : //www.ddj.com/cpp/184401197(関数についてですが、ここでも適用できます)。