equal_l2’s blog

※記載されている内容の正確性は保証しませんが、間違いを指摘していただければ直します。

ポインタを学ぶ(4) おまけ

あんまりポピュラーではないけど、ちょこちょこ出てくるテクニック。
(ポインタともあんまり関係ないけど)

条件分岐文の節約

0以上の整数入力に対して、1個づつ異なった値を、結果として返す処理を書いてみる。

以下のコードで共通の変数として、整数入力を格納する変数をint in;とする。

とんでもなく愚直に書くなら、if文を使って…

int in; //入力を受け取る変数
int data0 = ? ,data1 = ? ,data2 = ? , ...
...
if(in == 0){out = data0;}
else if(in == 1){out = data1;}
else if(in == 2){out = data2;}
...

Cを学ぶ上で割と最初に知るべきこととして、同じようなものを入れる変数は配列にまとめたほうが良い、ということがある。
この原則をdata0, data1, data2, ...に適用して、data[]にまとめる。
ただし、配列のインデックスと入力は対応するようにする。
たとえば、in == 0に対してはdata[0]が対応するようにする。
こうすると、次のように書き換えられる。

int in; //入力を受け取る変数
int data[] = { ... };
...
if( in >= 0 && in < sizeof(data)/sizeof(data[0]) ){
 out = data[in];
}
...

配列を扱うので、添え字に問題がないか(正の数か、要素数を超えていないか)をチェックする必要はあるものの、処理そのものは少なからずスマートになった。

ちなみに、sizeof(data)/sizeof(data[0])は、配列の要素数を求める非常にポピュラーな記法である。
標準Cではこの方法以外に配列の要素数を求める方法はない(はず)。
いちいち書くのが面倒なので、次のようにマクロにしてしまうパターンが多い。

#define SIZE(a) sizeof(a)/sizeof(a[0]) // 配列名を引数として取る

ポインタを出さないと記事名詐欺になってしまうので使い道を考えてみる。

もちろん基本型とか構造体へのポインタを配列に入れて上のような使い方をするのも便利なのだが、あまり新しい感じはしない。
むしろ、このテクニックは関数ポインタと結びついたときに真価を発揮する。

いま、0以上の整数入力に対して、1個づつ異なった処理を行うコードを書いてみる。
またif文を使って普通に書いてみる。

void f0(int arg){ ... }
void f1(int arg){ ... }
void f2(int arg){ ... }
...
int main(){
int a = ?;//関数に渡す引数
int in; //入力を受け取る変数
...
if(in == 0){f0(a);}
else if(in == 1){f1(a);}
else if(in == 2){f2(a);}
...

同じような処理を何回も書くのは癪だ。

そこで、関数ポインタの出番だ。
同じ戻り値の型、引数の型・数を持つ関数なら(定義は個別にしなければならないが)これでまとめられる。

void f0(int arg){ ... }
void f1(int arg){ ... }
void f2(int arg){ ... }
...
int main(){
int a = ?;//関数に渡す引数
int in; //入力を受け取る変数
void (*f[])(int) = { f0, f1, f2, ... };
...
if( in >= 0 && in < sizeof(f)/sizeof(f[0]) ){
 f[in](a);
}
...

この記述の素敵なところは、何個も何個も同じような記述を繰り返さなくて済むことだ。
関数ポインタを使えば、引数を書く回数も減らせる。

書く回数を減らすことは、ミスを減らすことにつながる。

2016/2/3 コードに間違いがあったので修正