グローバル変数 v.s. 構造体
ドーナツがグローバル変数を使うべきでない理由で迷っているので。
俺は、グローバル変数はできるかぎり避けるべきだと思っている。なぜなら、グローバル変数よりも構造体のほうが拡張しやすいからだ、と言っても、ピンとこないだろうから、具体的な例でやってみよう。
お題は、あるファイルに含まれている文字数を数えるプログラム。そして、そのあと、'a'を数える機能を追加する。それと、グローバル変数や構造体の例として、名前と値を一つにまとめたカウンタという概念を使ってみる。
グローバル変数版
まずは、グローバル変数を使ったバージョン。
#include <stdio.h> static int num_; static const char* name_; void set_name(const char* name){ name_ = name; } void inc_counter(){ num_++; } void print_counter(){ printf("%s:%d?n",name_,num_); } main(){ FILE* fp = fopen("test.txt","r"); set_name("char counter"); while(getc(fp) != EOF){ inc_counter(); } print_counter(); fclose(fp); return 0; }
プログラムを単純にするために、ファイル名を固定した。一応、実行例をしめしときます。
$ counter char counter:106
構造体版
次に、構造体を使って書き直します。
#include <stdio.h> typedef struct{ const char* name; int num; } Counter; void set_name(Counter* counter,const char* name){ counter->name = name; } void inc_counter(Counter* counter){ counter->num++; } void print_counter(Counter* counter){ printf("%s:%d?n",counter->name,counter->num); } main(){ FILE* fp = fopen("test.txt","r"); Counter counter; set_name(&counter,"char counter"); while(getc(fp) != EOF){ inc_counter(&counter); } print_counter(&counter); fclose(fp); return 0; }
グローバル変数版 機能拡張
ここまでは、構造体を使っても、グローバル変数を使っても、大差はない。というか、むしろグローバル変数を使ったほうが、多少シンプルな気さえする。
で、ここからが本題。今、作ったプログラムに、'a'を数えるカウンタを追加してみる。
グローバル変数の場合は、編集する箇所が非常に広範囲になります。
#include <stdio.h> static int num_[2]; static const char* name_[2]; void set_name(const char* name,int i){ name_[i] = name; } void inc_counter(int i){ num_[i]++; } void print_counter(int i){ printf("%s:%d?n",name_[i],num_[i]); } main(){ FILE* fp = fopen("test.txt","r"); set_name("char counter",0); set_name("a counter",1); char c; while((c = getc(fp)) != EOF){ inc_counter(0); if(c == 'a') inc_counter(1); } print_counter(0); print_counter(1); fclose(fp); return 0; }
実行すると、以下のようになります。
$ counter char counter:106 a counter:7
構造体版 機能拡張
構造体の場合は、新しい変数をつくるだけで、ほとんどコードを変更せずに機能を追加できる。
#include <stdio.h> typedef struct{ const char* name; int num; } Counter; void set_name(Counter* counter,const char* name){ counter->name = name; } void inc_counter(Counter* counter){ counter->num++; } void print_counter(Counter* counter){ printf("%s:%d?n",counter->name,counter->num); } main(){ FILE* fp = fopen("test.txt","r"); Counter cc; // char counter Counter ac; // a counter set_name(&cc,"char counter"); set_name(&ac,"a counter"); char c; while((c = getc(fp)) != EOF){ inc_counter(&cc); if(c == 'a') inc_counter(&ac); } print_counter(&cc); print_counter(&ac); fclose(fp); return 0; }
まとめ
- 作者: ブライアンカーニハン,ロブパイク,Brian Kernighan,Rob Pike,福崎俊博
- 出版社/メーカー: アスキー
- 発売日: 2000/11
- メディア: 単行本
- 購入: 58人 クリック: 1,152回
- この商品を含むブログ (209件) を見る
というわけで、「グローバル変数よりも、構造体を使うべきだ」と主張させて頂きます。
あと、この話は、プログラミング作法という本に載っていました。
あと、C++の場合、異なるファイルにあるグローバル変数は初期化順序が保証されないという問題があるんだけど、それはまた別の話。(気になるならEffectiveC++でも読みぃ)