記憶クラス
auto指定子
C言語では、変数の保存方法をさらに細かく指定することができます
この、変数の保存方法の詳細を指定するのが記憶クラス指定子です
C言語の中でもっとも意味のないキーワードのひとつでもあるのですが
自動変数であることを指定するauto指定子が存在します
リファレンスでは見かけますが、実用されているソースはほとんど見たことがないでしょう
自動変数とはローカル変数のことであり、C言語では指定する必要はありません
この指定子は過去の言語の互換性(バックワード・コンパティビリティ)のために残されているのです
過去の言語とは、C言語の前のB言語との互換性らしいです
#include <stdio.h>
char str;
int main() {
auto char str[] = "Kitty on your lap";
printf(str);
return 0;
}
auto指定子は、この意味で使われることはほとんどないでしょう
複数のファイルをコンパイルする
これまでのサンプルソースは単一のファイルをコンパイルしていきました
しかし、実用的なアプリケーションの場合はそうはいかないでしょう
内容が複雑になれば、当然ソースも長くなります
その場合は、複数のファイルにプログラムを分けて開発を進めます
共同開発の場合は、なおさらそうなりますね
この時の問題がグローバル変数の扱いです
次の二つのファイルのプログラムをコンパイルしてみましょう
複数のファイルのコンパイル方法については、ご自分の開発環境のヘルプをご覧ください
/*test1.c*/
#include <stdio.h>
char *str;
void outString(void);
int main() {
str = "Kitty on your lap\n";
printf(str);
outString();
return 0;
}
/*test2.c*/
#include<stdio.h>
void outString() {
printf(str);
}
これをコンパイルすると、次のようなエラーメッセージが返されるはずです
未定義のシンボル str(関数 outString )
test2.cファイルのほうで、変数strが認識されていません
グローバル変数でも、ファイル間では認識されないということがわかりますね
そこで、次のようにtest2.cファイルを変更してコンパイルしてみました
/*test2.c*/
#include<stdio.h>
char *str;
void outString() {
printf(str);
}
こうすると、2ヶ所で同じシンボルが宣言されていることになってしまいます
そのため、コンパイルエラーが起こるか警告が出るでしょう(筆者の環境では警告)
実行できたとしても、グローバル変数strに互換性がないので強制終了になります
test2.cファイルのstrには何も格納されていないので、文字出力時にヌル文字がないためです
この問題を解決するには、記憶クラス指定子extern指定子をしようします
これは、明示的に変数が別の場所(つまりグローバルな位置)ですでに宣言されていることを表します
この指定子は、コンパイラに変数の位置を教えるだけで新しいメモリ領域を割り当てません
先ほどのプログラムを完成させると、次のようになります
#include <stdio.h>
char *str;
void outString(void);
int main() {
str = "Kitty on your lap\n";
printf(str);
outString();
return 0;
}
#include<stdio.h>
extern char *str;
void outString() {
printf(str);
}
これで、二つのファイルのグローバル変数strは同じアドレスを指します
ひとつのプロジェクトで複数のファイルに分割する場合は、このようにグローバルな値に注意を払う必要があります
もちろん、同一ファイルでも関数内の変数でグローバル変数を明示的に表すのに
extern指定子を使って、グローバル変数の参照を持つ変数を宣言することもできます
が、ローカル変数で宣言されていない変数は、自動的にグローバル変数を参照しようするので
そのような意味でextern指定子が使われることはありません
レジスタを使う
C言語では、CPUのレジスタを使用してアクセス速度の向上を図ることができます
これは大変高度な技術といえますが、場合によってはアプリケーションの実行速度を大きく向上させることができます
CPUとレジスタについては、ハードウェアアーキテクチャの分野なのでここでは語りませんが
レジスタとは、CPUからのアクセスがもっとも高速な記憶装置であるとだけ覚えてください
CPUがダイレクトに操作、参照できるレジスタは高速ですが領域は小さく限りがあります
そのため、必ずレジスタを使えば速くなるというわけではありません
値をレジスタに保存するにはregister指定子を使います
何度も言いますが、レジスタには限りがあるので、範囲を超えるとコンパイラが自動的に普通の変数に変更します
一般的には1つの関数で2つ程度の変数をレジスタに格納するのが最適とされます
#include <stdio.h>
int main() {
register int i , d = 10000;
for (i = 0 ; i < 10000 ; i++) {
printf("i = %d : d = %d\n" , i , d);
d--;
}
return 0;
}
あんまり意味はありませんでした…
でも、場合によっては速くなるかも♪
関数をregister宣言することもあります
初期化されないローカル変数
通常のローカル変数は、関数が呼び出されるごとに初期化されていました
つまり、常にローカル変数は使い捨て変数であると言えます
次のプログラムを見てください
#include <stdio.h>
void func(void);
int main() {
int count;
for (count = 0 ; count < 1000 ; count++) func();
return 0;
}
void func() {
int count = 1;
printf("%d\n" , count);
count++;
}
func()関数で変数countを宣言し、処理を終えるとインクリメントします
呼び出されるごとにインクリメントを繰り返し、カウンタとして値が増えつづけることを期待しますが
count変数は、関数終了時に消滅しています
呼び出された時には、再び初期化されているので画面には1だけが連続して出力されます
環境によっては、コンパイル時に警告が出るでしょう
関数が呼び出されると値が増えるといった処理は
呼び出し側で制御するか、グローバル変数を利用する必要があります
しかし、アルゴリズムによってはこれをローカル変数で扱う必要があるかもしれません
外部の値に依存しなければいけない関数は、完成されたモジュールとは言いがたいですね
そこでstatic指定子を使用します
この記憶クラスは変数の作成時のみ初期化します
それ以降、この変数は関数が修了しても破棄される事はありません
また、関数を呼び出しても初期化されることはありません
#include <stdio.h>
void func(void);
int main() {
int count;
for (count = 0 ; count < 1000 ; count++) func();
return 0;
}
void func() {
static int count = 1;
printf("%d\n" , count);
count++;
}
これで、func()関数は呼び出された回数分の値を出力します
関数が終了しても消滅させたくない変数の作成には、static指定子を用いてください
auto declarator
変数の範囲がローカル(自動)変数であることを明示的に表します
extern
変数が現在のファイル以外で定義されていることを明示します
別のソース ファイルで定義しても、同一ファイルで後で定義してもかまいません
この変数はプログラムの開始時に割り当てられ、プログラムの終了時に解放されます
register declarator
状況が許す限り、変数をマシンのレジスタに格納するように指定します
static declarator
指定した変数を静的な期間で割り当てます
変数は初期化されるとプログラムが終了されるまで維持され
プログラムの終了時に解放されます