はじめに
こんなコードを見た。
#include <iostream>
int main(int, char**) {
std::cout << "Hello, world!\n";
}
あれっ?main()の引数に名前がないぞ?これ使えるの?答えは「使えない」である。ではなぜ引数名を省略するのか?
ググってみた
いくつか理由はあるらしい。
- 将来のために引数を予約しておきたい
- 関数をオーバーロードしたときに引数の数で動作を変えたい
その他に「プロトタイプの宣言では引数を省略する」というのが出てきた。どういうことだ?
次のコードを考えてみよう。
#define hoge 1
#include "func.hpp"
int main()
{
func1(3);
return 0;
}
int func1(int hoge);
int func2(int fuga);
このコード、コンパイルが通らない。では、これをプリプロセッサにかけてみよう。
$ cpp main.cpp
# 1 "./func.hpp" 1
int func1(int 1);
int func2(int fuga);
# 3 "main.cpp" 2
int main()
{
return 0;
}
あれっ?関数のプロトタイプがおかしくなっってる?はい、そのようである。
これは、main.cppの1行目の #define が、問題を起こしてしまった。'hoge'という識別子と競合してしまったのだ。通常ライブラリを使うときにはヘッダファイルを隅々まで読んで使うことはない。たまたま自分が使ったマクロ展開がライブラリ内の識別子とぶつかってしまうことは可能性としては起こりうる。
今回の例ではそもそもコンパイルが通らないのでまだ問題は発見しやすいが、
#define hoge []
なんて書いてしまったら、文法上は正しいことになる。もちろん呼び出してみればエラーにはなるが、もうその理由は意味不明であろう。
そういえば?
おそらくC言語系を学んだ皆さんは、習慣的に#defineは#includeとかが一通り終了した後に書いているのではなかろうか。また、特別な理由がない限りソースコードの途中でなにかのファイルをインクルードすることはしないはずだ。それはほとんど習慣だったかもしれない。
私もなぜそうしているのかを深く考えたことはなかった。ひょっとしたら当時の師匠には理由を説明されたかもしれないけれど、初心者な私はマクロ展開なんてわかっちゃいなかった。
では解決してみよう。
#include "func.hpp"
#define hoge 1
int main()
{
func1(3);
return 0;
}
めでたい!解決である。
セキュリティ上の問題
JPCERTのページにこんなのがある。
なるほど、場合によってはセキュリティ上のリスクになるのか、それは気が付かなかった。
ということで、普段コードを書くときは #define は後ろに、ヘッダファイルを書くときは引数名は書かないように、ということなのだろう。