見出し画像

C言語教室 第33回 static ってなにさ?

関数の外側で宣言した変数はグローバル変数と呼んで、同じソースファイルに含まれるどの関数からも読み書きできるだけではなく、他のソースファイルからも extern 宣言をすることで使えることは説明しました。

C言語には同じソースファイルに含まれる関数からはグローバル変数として振る舞うのだけど、他のソースファイルからは extern することが出来ないファイル内のみグローバルな static 宣言をした変数というものがあります。

再び第1回の平均値を求める関数を例に使いましょう。

#include <stdio.h>

static int a;
static int b;

void ave() {
  int v;
  v = (a + b) / 2;
  printf("Ave(%d,%d)=%d\n", a, b, v);
}

void main() {
  a = 2;
  b = 9;
  ave();
}

このようにaとbの宣言の最初にstaticを付けると、これらの変数はファイルの中だけで、どの関数からも読み書きできます。これをave関数をave.c、main関数をmain.c と2つのファイルに分けた場合、たとえmain.cにextern int a; と書いてもaを使うことはできません。main.cにもstatic int a;と書いた場合、それはmain.cのみで使えるaであって、ave.cでstaticに宣言したaとは別の変数として扱われます。

このようにグローバル変数とローカル変数の狭間にあるのがstatic宣言された変数です。グローバル変数というのはすべてのファイルでひとつの変数となるので、どこから書き換えられてしまうのかが分かりにくいですし、大きなプログラムになるとうっかり同じ名前の変数を使ってしまいエラーになるか(型が異なる場合)思っていなかった動作をする(型が同じだった場合)原因となることがあります。とはいえすべての変数を関数を呼び出すたびに引き数に積んで使うのは、面倒なだけではなくパフォーマンスが問題になることもあります(引き数に積むにもコピーするにもコストがかかる)。

そこで密接な関係のある関数の間でだけ共有することができるstatic変数は、便利に使えることもよくあります。他のファイルで使うことができないのですから、同じファイルの中でだけ注意すれば名前がぶつかることも避けられますし、うっかりどこかで書き換えられる心配もありません。

しかし、どうしてこれが static と呼ばれるかはなかなか謎ですよね。これについてもう少し説明してみましょう。


C言語の関数内で普通に宣言して使われる変数はローカル変数と呼び、これらの変数はスタック領域に関数が呼び出された都度、確保されて使われます。またmalloc関数で確保される動的な変数の中身はヒープ領域と呼ばれるメモリが使われます。これに対してグローバル変数は、プログラムが走る際に最初に特定の領域が指定され、その決まった場所が使われます。

さて static にはもうひとつの使い方があります。ローカル変数の宣言で static を付けると、それは静的変数と呼ばれ、スタックではなく特定の領域に確保された変数になります。スタックではないので、関数を呼び出してリターンした後でも、その値は残り続けます。つまり静的な変数には、宣言した関数の中でしか読み書きできないものの、その寿命はプログラムの開始から終了まであるのです。

ローカルな静的変数を使用したコードを示します。

#include <stdio.h>

void myFunction() {
  static int x = 0;
  x++;
  printf("%d\n", x);
}

void main() {
  myFunction();
  myFunction();
  myFunction();
}

このコードを実行すると

1
2
3

と出力されます。このようにローカルな静的変数は、関数が呼び出された後でも値が保持されます。

このような特性があるため、主に状態の保持のために静的変数は使われるのですが、スタックを消費しないために、エラー処理であるとかスタックがタイトな組み込みなどでは通常のローカル変数を静的にすることもあります(稀にスタックが破壊されるような処理がある)。

つまり有効なのは関数の中だけのグローバル変数として機能しているのです。同じように関数の外側に書いたstaticな変数も寿命はグローバル変数と同じようにプログラムの開始から終了までなのですが、有効なのは同一ファイルの中のみのローカルなんだよという意味でstaticと書くのです。static と書いていないグローバル変数も静的に確保されていているのですが、何故かstaticなんですよね。

さらに関数名に static を付けると変数のときと同じように、同じソースファイル内からしか呼び出すことができなくなります。つまり static というのは、グローバルな名前に対して「制限をかけるよ」という意味なんだと解釈すれば良さそうです。使うことができる場所が制限されるのであって、実際に「静的に」プログラムの開始から終了までの間、その中身自身は存在します。


おまけ

グローバル変数やstaticで宣言された静的な変数は、初期値がある場合は、予めその値が実行ファイルの一部に書き込まれていて、プログラム開始時にファイルに含まれているそれらの値を特定の領域に読み込んで変数として割り当てます。初期値がない場合は、実行ファイルに覚えておく必要がないので、プログラム開始時に必要な大きさの領域を確保して、内容をゼロにクリアしてから変数に割り当てています。この開始時に割り当てるメモリのことをBSS領域と呼ぶことがあります。

.bss

これが理由でグローバル変数や静的変数は初期化していなくても0の値となっているのですが、これに頼るコーディングはオススメしません。変数は使う前に必ず初期化しましょう。


さて、今回も課題はありません。次回はまだ説明していない宣言や変数で出てくる予約語たちを済ませる予定です。

ヘッダ画像は、いらすとや さんよりhttps://www.irasutoya.com/2015/04/blog-post_452.html

#C言語 #プログラミング講座 #static #静的変数 #有効範囲 #寿命 #bss

この記事が気に入ったらサポートをしてみませんか?