第37回 プログラミングについて 『好みの問題なのだが...』

『好みの問題なのだが...』
C言語でプログラムでプログラムを書いていると、時々どっちにしようかと迷うことがあります。もしかしたら私だけなのかも知れませんが...。
まず迷うのが、for ループを組むときに、
for(i=0;i<=9;i++){ ... }
とするのか、
for(i=0;i<10;i++){ ... }
とするのかです。どちらも10回繰返されますが、最初のものはどちらかと言えば配列の要素のアクセスをするのが主になるループのときに使った方が良いような気がします。
2番目のものは、10回繰返すのだというときに使った方が良いようなきがします。でも、普段プログラムを書くときにはこの差はあまり気にしていないのが実状です。
FORTRANの場合は配列の要素は1から始まるので、
DO I=1,10
と書くことに何も疑問はないと思います。まさか特別な理由がない限り10回繰返す目的に、
DO I=0,9
と書く人はあまりいないと思います。
次に迷うのは、
int data[10];
int ndata;
ndata=0;
data[ndata++]=1;
と書くのか、
int data[10];
int ndata;
ndata= -1;
data[++ndata]=1;
と書くのかです。多分最初からC言語でプログラムを学んだ人は最初の書き方をすると思います。ところがFORTRANからスタートした私は、
INTEGER DATA(10)
INTEGER NDATA
NDATA=0
NDATA=NDATA+1
DATA(NDATA)=1
という書き方が身にしみついているせいか、次の配列の要素に値を代入するときには2番目の書き方をしたくなるのです。これも好みの問題なのだとは思うのですが、どうしても代入してから要素番号をインクリメントするのが不自然に思えるのです。
値をただインクリメントするときにも配列の要素のインクリメントと同様に、
i++;
とするのか、
++i;
とするのかは迷うところです。これも好みで、人によって様々です。
まだ迷うことがあります。関数の戻り値です。成功したか否かを戻り値とする関数を作るときに、成功したときに1にして、失敗したときには0にするのか、成功したときに0にし、失敗したときに1にするのかです。私の場合0と1の他に、成功が1で失敗が-1という関数もよく作ります。これも関数の性格にも左右されるのですが、例えば文字列が一致しているかいないかを判定する関数のときには、
if(match_string(string1,string2)){ ... }
と記述した方が自然ですので、合っているときには1そうでないときには0とした方するべきです。
ファイルにデータを書き込む関数のときでは、成功を1、不成功を0とすると、
if(!write_to_file(...)){
printf("Error!\n");
}
となりこれも自然ですが、不成功のときにその理由を示す数値で返すとすると、
if((sts=write_file(...)){
if(sts==1){
printf("No such file!\n");
}
else if(sts==2){
printf("Device full!\n");
}
else if( ... ){
:
:
}
}
成功が0で、不成功のときにはエラーの内容に応じた0以外の数値を戻り値にした方が良さそうです。
関数の型を決めるときには迷います。FORTRANで言えばサブルーチンと呼ばれる関数で、戻り値には何も意味がないとき(いつも同じ戻り値しか返さない関数)に、C言語では、
sub(...)
{
:
:
}
としてもコンパイラには叱られません。この場合は関数 sub は int 型で戻り値は不定です。がC++で書くときには戻り値が不定だと叱られてしまいます。そこで、
sub(...)
{
:
:
return 1;
}
などと、1を返すようにするのか、
void sub(...)
{
:
:
}
関数の型を void 型にしてしまうかです。本来ならば関数が値を返さないときには void型にして明示的にするべきなのですが、ちょっとしたプログラムを書くときにはこれも面倒ですので、ついいい加減にしてしまいがちなのです。
変数の名前の付け方にも迷うことがよくあります。例えばX座標とY座標を保持する変数名を考えるとき、
double xpos,ypos;
とするのか、
double posx,posy;
とするのか、要するにXとYの文字を最初に置くのか後に置くのかです。これもどちらでもいい話です。
for ループにするのか、while ループにするのかで迷うこともあります。
for(i=0;i<last;i++){ ...}
を、
i=0;
while(i<last){
i++;
}
と書くことはまずないとは思いますが、構造体のメンバーが次の構造体を指し示すとき
などには、
for(p=data;p!=NULL;p=p->next){ ... }
とするか、
p=data;
while(p!=NULL){
:
:
p=p->next;
}
とするのかは迷うところです。ただ、ループ内でデータの内容に応じてそれ以降の処理を中止し、次の構造体を処理するときには、
for(p=data;p!=NULL;p=p->next){
:
:
if(p->x>xmax)continue;
:
:
if(p->x<xmin)continue;
:
:
}
とした方が、
p=data;
while(p!=NULL){
:
:
if(p->x<=xmax){
:
:
if(p->x>=xmin){
:
:
}
}
:
:
p=p->next;
}
とするよりもループ内がスッキリしますので、for ループを使用します。
コメントの書き方にも迷うことがあります。
/* 111 */
/* 222 */
/* 333 */
複数行になるコメントをこのように書くのか、
/* 111
222
333 */
それとも、
/*
111
222
333
*/
と書くのかです。これもどうでもいい話です。C++には // でコメントを書けるので、
// 111
// 222
// 333
とするのかも迷ってしまいます。
これはエディタの問題なのかも知れませんが、インデントは最近では4(4タブ)にしています。以前はずっと8で行なっていました。8だとインデントが深くなると文が右に寄り過ぎて、一番深い部分では10文字ぐらいしか書けなくなることもしばしばでした。OSは通常8が標準になっていますので、インデントが8のときには画面に表示してもリストが乱れないので便利なのですが、プログラムを書くときにはやっぱり辛いものがあります。それで最近は4にしている訳です。2ですとインデントしているのかいないのかが分かりずらいので(説明に使用しているプログラムの例では2で記述していますが....)私は使っていません。
今回はつまらない話でしたが、多分誰でもプログラムを書いているときに同じようなことで一瞬迷うことがあるはずです。だからと言ってこれらに規則を作って自分自身を縛り付けるのもどうかと思います。多分このどうでもいいことはこのままどうでもいいことにしておくのが一番いいような気がします。
ではまた次回。