💡

[C++] 3つの静的変数の違い

2023/11/29に公開

C++で静的変数というと以下の3種類がある。

  • 非staticグローバル変数
  • staticグローバル変数
  • staticローカル変数

これら3つについてどのような差があるかを整理した。

非staticグローバル変数

// temp.cpp
int globalValue { 5 }; // 非staticグローバル変数
// temp.h
extern int globalValue;
// main.cpp
#include "temp.h"
int main()
{
  std::cout << globalValue1 << std::endl; // 参照可能
  return 0;
}

グローバル領域(関数内等ではない領域)にstaticのキーワードを付けないで変数定義される変数。
変数の初期化タイミングはmain関数の実行前。
ヘッダーでextern宣言してあげれば他のファイルから変数を参照して使うことができる。
複数の翻訳単位に複数のグローバル変数が定義されている場合、どのような初期化順になるかは明確に決まっていないので、初期化順序に起因する問題が起きないように気を付ける必要がある。

staticグローバル変数

// temp.cpp
static int globalValue { 5 }; // staticグローバル変数
// temp.h
// extern int globalValue; // externをするとコンパイルエラーになるのでできない
// main.cpp
#include "temp.h"
int main()
{
  // std::cout << globalValue1 << std::endl; // 参照不可能
  return 0;
}

グローバル領域(関数内等ではない領域)にstaticのキーワードを付けてで変数定義される変数。
変数の初期化タイミングは非staticのグローバル変数と同じなので、初期化に関しての問題も同じ。
他のファイルから変数を参照して使うことができない。(変数定義しているファイル内でのみ変数の参照が可能。)

staticローカル変数

// temp.cpp

int testfunc()
{
  static int localStaticValue { 5 }; // ローカルstatic変数
  return localStaticValue;
}

int testfunc2()
{
  int temp;
//  temp = localStaticValue; // コンパイルエラー。参照できない
  return temp;
}

関数内等のローカル領域にstaticで定義される変数。
変数の初期化タイミングは初めて変数にアクセスされたタイミング。
そのため、初回アクセス時は初期化処理が実行されるが2回目以降は初期化処理は実行されない。
関数の処理が終わっても変数の値は保持される。
スコープ外から直接変数を参照して使うことはできない。

補足

static変数は他のファイルから参照できないと書いたが、以下のように参照を取得する関数を用意することで、無理やり他のファイルからstatic変数を参照するというテクニックもある。

// temp.cpp
int& GetInstance()
{
    static int globalValue1 {1};
    return globalValue1;
}
// temp.h
extern int& GetInstance(); // ローカルstatic変数の参照を取得する関数
// main.cpp
#include "temp.h"
int main()
{
  std::cout << GetInstance() << std::endl;
  return 0;
}

Discussion