既定の実引数拡張

こんにちは、めのんです!

このブログのコラムは「PHPプログラマーのためのC講座」以上にマニアックな内容ばかり書いている気がしますが、気にせずに進めますね。
今回の話題はCの「既定の実引数拡張(default argument promotion)」です。
名前からしてややこしそうですよね。

既定の実引数拡張というのは仮引数並びが無い関数や可変個引数を持つ関数に実引数を渡す場合に行われる暗黙の型変換のことです。
何のことか分からない方もいらっしゃるかもしれませんね。

仮引数並びのおさらい

まずは仮引数並びのおさらいからです。
Cの関数を宣言する際には、丸括弧の中にカンマで区切った仮引数を並べると思います。
たとえば次のようにです。

int func(int a, long b, double c);

上の例ではa, b, cが仮引数になります。

一方でこのような仮引数並びがない関数宣言というのもあります。
こんな感じです。

int func();

この場合は仮引数並びがありませんので、どんな型の実引数を何個でも関数に渡すことができます。

仮引数並びが無い関数はとても古い形式で非推奨になっていますので、もしかすると一度も見たことがないという方もいらっしゃるかもしれません。
Cではこういう書き方もできるんです。

仮引数並びがある場合でも、可変個引数のための … がある関数というのもあります。
具体例としておなじみのprintf関数の宣言を見てみましょう。

int printf(const char* format, ...);

printf関数の宣言は上のようになっていて、最後に … がありますね。
これは可変個引数を受け取るための書き方です。

可変個引数の部分にも、どんな型の実引数を何個でも渡すことができます。
実際、printf関数はそういう使い方をしますよね。

既定の実引数拡張

さて、おさらいが終わったところで本題に入ります。

仮引数並びが無い場合や可変個引数に実引数を渡す場合、次のような暗黙の型変換が起きます。

  • 整数型に対しては整数拡張が行われます。
  • float型はdouble型に変換されます。

これだけです。
整数拡張はちょっとややこしいので以前の記事を参考にしてください。

この仕様を理解していれば、printf関数ではdouble型を書式化するために「%f」を使うのに、scanf関数ではなぜ「%lf」を使わないといけないのかが分かってきます。
printf関数の可変個引数にdouble型の実引数を渡してもfloat型の実引数を渡しても、既定の実引数拡張によってどちらもdouble型になってしまいます。
それに対して、scanf関数に渡すのはfloat型へのポインタであり、double型へのポインタです。

ポインタに対しては既定の実引数拡張で型が変わることはありません。
ですから、scanf関数ではfloat型に対する書式とdouble型に対する書式を変えないといけないのです。

いつも4,000字前後の長文になってしまうので今回はこの程度で終わりたいと思います。
何かご質問がございましたら、コメント欄に書き込んでいただくか、Twitterでご連絡いただけると幸いです。