static宣言とは
一言でいえば、
「プログラム実行中に消さないで同じ場所に保持しておいてね」ということです!
staticの言葉の意味は”静的”という意味です。
つまり、staticな変数を宣言すると静的な領域にデータを保持することになります。
static宣言された変数はプログラムの開始から終了までデータが保持されます。
逆にstaticを付けない場合は、動的な領域にデータが保持されます。普段は省略されますが、autoをつけて宣言できます。
static int a;
auto int b;
aは静的変数。 bは動的変数。
保存される場所
プログラムで使われるメモリ領域は4つに分かれます。
- テキスト領域(text)・・・機械語にコンパイルされたプラグラムが保存される領域。
- 静的領域(static)・・・初期値を持つ変数が保存される領域。
- ヒープ領域(heap)・・・プログラム内で動的に確保される領域。主にmalloc関数で確保する。
- スタック領域(stack)・・・OSやコンパイラがプログラムで使う値を保存する領域。
staticで宣言した変数は静的領域に確保されます。
単なるグローバル変数も同じ領域に確保されます。
関数内のローカル変数や関数の引数、戻り値はスタック領域に確保されます。
ヒープもスタックも一時的に確保される領域なので、プログラムの内容によって消えたりします。
一方で、staticで宣言した変数はずっと同じ場所にいます。
staticを使用する場面
こんなstatic変数の使いどころは主に2点です。
同じ値を使いまわしたい
同じ値を使い回したい場面で有効に使えます。
例えば、ある関数を呼ばれた回数をカウントした時のカウンターをintで定義する場合に使えます。
実際にコードを書いてみます。
引数で受けた値によってstatic変数で宣言された変数をインクリメントしたり、表示したりする関数を定義してみます。
count.h
#include <stdio.h>
enum operation
{
PRINT = 0,
ADD = 1,
RESET = 2
};
void function_a(int operation);
count.c
#include "count.h"
void function_a(int operation)
{
static int cnt = 0;
switch (operation)
{
case 0:
printf("cnt=%d\n", cnt);
break;
case 1:
cnt++;
break;
case 2:
cnt = 0;
break;
default:
printf("Undefined operation was called.\n");
break;
}
}
main.c
#include "count.h"
int main(void)
{
function_a(PRINT);
function_a(ADD);
function_a(PRINT);
function_a(ADD);
function_a(PRINT);
function_a(RESET);
function_a(PRINT);
return 0;
}
これらをgccを使ってコンパイルしてリンクします。
$ gcc -c count.c
$ gcc -c main.c
$ gcc -o eexe main.o count.o
$ ./exe
cnt=0
cnt=1
cnt=2
cnt=0
function_a内で定義された変数cntはfunction_aが呼ばれた初回のみ0で初期化されます。
main関数から何度もstatic int cnt=0の行を通りますが、この行は初回のみ実行されます。
なので、ここではローカル変数を静的な領域に確保するという意味でstaticを使っています。
関数実行時に
前回実行した時の値を使いたいんだよなあ
そんな時に使えます。
また保存される領域としてはグローバル変数と同じなので、グローバル変数として定義すればいいのではないかと思われるかもしれません。
しかし、関数内で定義する変数はあくまでもローカル変数なので、その関数の中からしかアクセスできません。
よりアクセス範囲を限定したいときに有効です。
別のファイルからの使用を防ぐ
もう一つのstatic宣言の使い方はグローバル変数を宣言したファイル以外からのアクセスを防ぐことです。
先ほどのcountの例だとmain.cを
#include "count.h"
extern int cnt;
int main(void)
...
として、外部に定義されているグローバル変数cntを宣言し、
count.cの方を
#include "count.h"
int cnt = 0;
void function_a(int operation)
...
として、グローバル変数を定義する場合、特に問題なくコンパイル、リンクが通ります。
しかし、count.cを
#include "count.h"
static int cnt = 0;
void function_a(int operation)
...
という風にstaticで宣言すると、次のようなエラーがでてリンクがうまく行きません。
main.c:(.text+0x50): undefined reference to `cnt'
これは、main.cからextern宣言されたcntを探そうとしたけど定義が見つからなかったということです。
まとめ
今回はstatic宣言の意味について書いてみました。
個人的にはstatic宣言を関数の中に定義することで初回のみ初期化が実行されることに最近感動しました。
ただ、グローバル変数と保存される領域は変わらないので、おおよそグローバル変数と同じだがアクセス範囲を限定できるという意味でより安全な定義になると理解しました。
以上、皆さんの参考になれば嬉しいです。
コメント