static の解説と実装リファレンス
【概要】
Arduino(C/C++環境)における static は、関数や変数に対して「生存期間(Lifetime)」および「スコープ(Scope)」を制御するための記憶クラス指定子である。Arduinoのコンテキストでは、主に「関数内静的変数」および「ファイル内限定の静的変数・関数」の2つの挙動を指す。
関数内で定義された static 変数は、関数を抜けてもその値を保持し、次回の呼び出し時に前回の値を引き継ぐ。一方、ファイルレベルで定義された static は、そのファイル(翻訳単位)内でのみ参照可能となる。
【仕様】
1. 関数内静的変数 (Static Local Variables)
- 初期状態: プログラムの初期化段階で一度だけ初期化される。関数が実行されるたびに初期化されるわけではない。
- 生存期間: プログラムの実行期間全体(プログラム終了まで)。
- スコープ: 定義された関数内のみ。他の関数からは直接アクセスできない。
- データ型: 基本型(int, float等)、構造体、配列など、あらゆる型が指定可能。
2. ファイル内静的変数・関数 (Static Global Variables/Functions)
- 初期状態: プログラムの初期化段階で初期化。
- 生存期間: プログラムの実行期間全体。
- スコープ: 定義されたソースファイル内(内部リンク)。他のファイルから
externを用いて参照することはできない。
【技術的特徴】
メモリ配置とスタックの比較
- 自動変数 (Automatic Variables): 通常の関数内変数はスタック領域に配置され、関数の実行終了とともにメモリが解放される。
- 静的変数 (Static Variables):
staticを付与した変数は、スタックではなくデータセグメント(.dataまたは.bss)に配置される。このため、関数の呼び出しと終了に関わらず、メモリ領域が確保され続ける。
他の修飾子との比較
| 修飾子 | 生存期間 | スコープ | 主要な用途 |
| (なし) | 関数内のみ | 関数内 | 一時的な計算用変数 |
| static | 全期間 | 関数内 | 状態の保持、カウンター |
| static | 全期間 | ファイル内 | カプセル化、名前の衝突防止 |
| volatile | 全期間 | 任意 | 割り込み等による意図しない変更への対策 |
【注意点】
- 初期化のタイミング:
static変数の初期化は起動時に一度だけ行われる。ループ処理の中で「初期化」を意図する場合は、初期値の代入ではなく条件分岐による制御が必要である。 - メモリ制約: Arduino(特にUnoやNanoなど)はSRAMが非常に限定的である。大きな配列を
staticで定義すると、プログラムの実行中常にそのメモリを占有するため、メモリ不足(Stack OverflowやHeapの枯渇)の原因となる。 - 再入可能性 (Re-entrancy): 静的変数は複数のパス(特に割り込みハンドラやマルチスレッド環境)から共有される可能性がある。Arduinoの標準的な環境ではシングルスレッド動作が基本だが、複雑な設計においては競合に注意が必要である。
- 初期値の保証: C++の仕様により、初期化されない
static変数はゼロ初期化されるが、意図しない挙動を防ぐため明示的な初期化を推奨する。
【サンプルコード】
/* 関数を呼び出すたびにカウントが増加する例 */
int getCallCount() {
// 関数を抜けても値が保持される静的変数
static int count = 0;
count++;
return count;
}
void setup() {
Serial.begin(9600);
}
void loop() {
Serial.print("Call Count: ");
Serial.println(getCallCount());
delay(1000);
}
【活用例】
- 状態遷移マシン(State Machine): 前の状態を関数内で保持し、次なる状態への遷移を判定する処理に最適。
- 簡易カウンター: ボタンの押し込み回数や、センサーのサンプリング回数を、グローバル変数として公開せずに特定の関数内だけで管理する場合に有用。
- ドライバの実装: ハードウェアのレジスタ状態をキャッシュする変数をファイル内
staticとして定義することで、他のソースファイルからの不要な干渉を防ぎ、カプセル化を強化する。


コメント