01: プログラミング言語とは何か 02: 0から99まで印字するプログラムの解説 03: Pythonで書くと... 04: 変数と関数 05: 条件分岐とループ 06: プリプロセス 07: ポインタと配列1 08: ポインタと配列2 09: ポインタと配列3 0A: 文字列 0B: 多次元配列 0C: ファイル入出力 0D: 構造体 0E: コマンドライン引数 0F: 数値を読み込んで総和と平均を表示するプログラム 10: 数値を読み込んで総和と平均を表示するPythonプログラム 11:【番外編】文章中の単語を出現頻度順に表示するプログラム 12:【番外編】コマンドライン引数を処理するイディオム 13:【番外編】C言語でクロージャを実装する
C言語プログラムは変数と関数のフラットな寄せ集めである.フラットという意味は,例えば関数の中に関数があったり,ある変数が特定の関数の中に埋め込まれているということが無いという意味である.関数はちょっとした例外を除けば静的(コンパイル時に作られる)である.変数は静的なものと動的なもの(プログラム実行時に作られる)ものがある.動的はC言語では自動的(auto)と言い直すことになっている.
静的な変数から行こう.プログラムの地の文で
int a = 0;
と書かれていれば,それは静的な変数の定義(メモリが確保される)である.(int型すなわち整数で,初期値が0である.)これにはバリエーションがあって,
int a;
と書いても静的な変数の定義である.初期値は与えられるが,後で紹介する自動変数の省略記法と同じになってしまう.こんな書き方はしてはならない.初期値無しが許される場合もある.次の宣言を見てみよう.
extern int a;
これは,int型のaという変数があるが,このファイルではなくどこか別のファイルにあるという意味だ.メインのファイル(例えば main.c としよう)と別のファイル(例えば another.c としよう)があるとして,main.c では extern int a; としておき,another.c の中で int a = 0; としておく.コンパイルするときに
$ gcc main.c another.c
とすれば,無事両方のファイルが変数aを共有する.地の文に書いた int a = 0; はデフォルトで他のファイルからも参照できる.他のファイルは勝手に extern int a; と宣言できるのだから.
もし,他のファイルから見られたくない変数があれば,
static int b = 0;
とstaticと書く.本来はprivateと書きたいところだが,staticと書くことになっている.static(静的)と書こうと書くまいと静的なのだが,プライベートという意味でstaticと書くことを認めないといけない.
次に静的な関数である.
C言語のプログラム上では,関数はいつも静的である.だから,特に気をつけることはない.静的関数(つまり普通の関数)は静的変数と同じくプライベートにすることが出来る.おなじみstaticキーワードを使う.
static int hello(void) {
...
return 0;
}
この関数helloは他のファイルから見ることが出来ない.注意!main関数をstaticにしないこと!main関数が外部プログラム(というかシェル)から見えていないと,プログラムを実行できない.
動的な変数,いや,自動的な変数について説明する.自動的な変数は,実行時に自動的にメモリが確保される変数である.典型的には,関数の引数と,関数の冒頭に定義する変数(局所変数とも言う)である.関数の中で,自動的な変数にはautoというキーワードをつける.
int bonjour(int a, int b) {
auto int x;
x = a + b;
return x;
}
ここで,a, b, x はすべて自動変数である.キーワードautoは「いつでも」省略出来る.(C++言語ではもはや別な用途にautoを使うので,自動変数を示すautoはもう二度と使われないだろう.)autoを省略すると見慣れた書き方になる.
int bonjour(int a, int b) {
int x;
x = a + b;
return x;
}
自動的な変数(ここでは a, b, x)は,太古の昔はメモリの中のスタックと呼ばれる領域に書き込まれていたため,未だにスタック変数と呼ぶ人もいる.自動変数は,引数として与えられたのでない限り(この例では a, b 以外は),初期化されない.
自動変数は関数が値を返すと消えて無くなる.関数の外からは,変数 a, b, x にアクセスできない.この意味で,自動変数は常に関数にプライベートな変数である.無理矢理公開しようとすると見つけにくいエラーとなる.例えば
int *konnichiha(int a, int b) {
int x;
x = a + b;
return &x;
}
という関数は自動変数のアドレスを関数の戻り値として返す.ただし,戻り値の使用はだいたい悲惨な結果に終わる.(こんなことは他の近代的な言語では出来ない.)
もう一度,静的な変数について述べる.
関数の中で,こっそりと静的な変数を定義することができる.ただし,この機能はよほどの事情がない限り使うべきではない.
int bonjorno(int a, int b) {
static int x;
x = a + b;
return x;
}
static int x; は関数の中で静的な変数を定義する書き方である.staticなので,自動的に関数にプライベートな変数になる.
静的変数はコンパイル時にメモリを確保してしまうので,不必要なメモリが取られるという意味でこの書き方は好ましくない.次に,自動変数と同じくプライベートであるので,関数内での数値の一時保存場所として使うのであれば,自動変数で十分である.最後に,関数内の静的変数を避けるべき最大の理由でもあるのだが,staticと書かれた静的変数はプログラムの実行時に一度だけ初期化される.自動変数が関数に入るたびに新たに作られるのに対して,staticと書かれた静的変数はもともとある変数であるため,ずっと元の値を持ち続けている.例えば次の関数は,呼び出されるたびに1ずつ大きな数値を印刷する.
void print_and_increment(void) {
static int i = 0;
printf("%d\n", i);
i += 1;
}
これは容易には見つからないエラーを引き起こすだけでなく,例えばプログラムの並列化や,最適化の妨げになる.関数内の静的変数は,本来プログラミング言語が備えているべきクロージャという概念の安い偽物である