なぜ引数は(int a, b)と宣言できないのか【深淵のプログラミング言語】


プログラミング言語の変数宣言では、一つの宣言式で複数の変数を宣言することが出来る。

int a, b, c;

ただ、仮引数の宣言部ではこれが出来ない。

// warning: type specifier missing, defaults to 'int' [-Wimplicit-int]
int add(int a, b) {
  return a + b;
}

なぜだろうか。

技術的には可能なはずなのである。しかしそれをあえてやらない理由があるのだろう。

目次

スポンサーリンク

必要性が無い

必要性の薄い軽微な機能であること、そして将来的な仕様拡張や仕様変更の際の柔軟性を担保する目的があると考えられる。

そもそも変数の宣言式と仮引数の宣言式は全く異なる目的や領域の上にあるため、両者に一貫性を求めること自体が無意味なのである。行き過ぎた一貫性の追求は柔軟性の排除に繋がる。

複数宣言は嫌われている

そもそもこの変数の複数宣言はプログラマの間では忌み嫌われている。

言語によっては一つの宣言に基本型やポインタ型、配列型を混在させることが出来てしまうためだ。

char character, *string, array[3];

初期値が絡めば更に読みづらく意図の掴みにくいコードが生まれてしまう。

また次の例も、誤解を招きやすいコードとして大変有名である。

char* a, b;

この場合の変数achar *a、つまりchar型のポインタ変数として宣言される。ただし、変数bについてはポインタ変数ではなく、char bつまり通常のchar型として宣言されてしまう。大変ややこしい。両者の具体的な違いは以下のページに書かれているので参考にされたい。

ポインタ型記法のススメ #ポインタ型記法のデメリット

つまるところ、現代ではこの複数宣言はバッドノウハウとして扱われているのである。そのためわざわざこのような粗悪な機能を仮引数宣言へ取り入れる理由も無いのである。

C言語の互換性問題

ただC言語については過去の互換性も大きく関係している。

昔のC言語では型名を省略することが出来たため、現代のコンパイラでも以下のようなコードが普通にコンパイル出来てしまう(もちろん警告は発生する)

add(a, b) { return a + b; }

この場合は引数a, bは暗黙的にint型として宣言される。add関数の戻り値型も同様にint型となる。

ちなみに各識別子に対して型名を明示することも出来る。

float add(a, b)
float a, b;
{ return a + b; }

これらはC言語仕様の負の遺産の一つとも言える。

そしてこれらの仕様を用いると、実は先程のint a, bをコンパイルすることが出来てしまう。

int add(int a, b) { return a + b; }

この場合、仮引数bはコンパイラの互換性によってint型の仮引数として認識されるようになる。

「なんだ、書けるじゃないか」

いいや、待って欲しい。以下のような場合には対応出来ない。

float add(float a, b) { return a + b; }

この場合の仮引数bfloat型にはならない。やはりint型として認識されてしまう。

そのため仮に第二引数にfloatの値を渡してもintの値として切り捨てられてしまう。

add(0.1, 0.1); // 戻り値: 0.1(0.2ではない?!)

よって、C言語では関数宣言時の仮引数で、変数宣言時のような宣言方法(int a, b)を利用することはできないし、この先もおそらく実現は難しいだろうと考えられる。

保守的なC言語

C言語は過去の互換性を重視するあまり、言語としての柔軟性と将来性を失っているようにも思える。しかし同時に、C言語は過去の資産と互換性を維持することで言語のシンプルさやコンパクトさを保つことが出来ているのである。これはC++言語とは対照的だ。

互換性という建前で国際機関から機能拡張を制限されているC言語。なんというか中二心をくすぐられるものがある。

俺「互換性という名の鎖から解き放たれた時、君は新たな次元へとシフトする」(キリッ!!)
C言語「うわ・・・なんか面倒くさいのが来た・・・」

広告