2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

C/C++で #defineは #include の後に書く理由?

Posted at

はじめに

こんなコードを見た。

#include <iostream>

int main(int, char**) {
    std::cout << "Hello, world!\n";
}

あれっ?main()の引数に名前がないぞ?これ使えるの?答えは「使えない」である。ではなぜ引数名を省略するのか?

ググってみた

いくつか理由はあるらしい。

  • 将来のために引数を予約しておきたい
  • 関数をオーバーロードしたときに引数の数で動作を変えたい

その他に「プロトタイプの宣言では引数を省略する」というのが出てきた。どういうことだ?

次のコードを考えてみよう。

main.cpp
#define hoge 1
#include "func.hpp"

int main()
{
    func1(3);
    return 0;
}
func.hpp
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とかが一通り終了した後に書いているのではなかろうか。また、特別な理由がない限りソースコードの途中でなにかのファイルをインクルードすることはしないはずだ。それはほとんど習慣だったかもしれない。

私もなぜそうしているのかを深く考えたことはなかった。ひょっとしたら当時の師匠には理由を説明されたかもしれないけれど、初心者な私はマクロ展開なんてわかっちゃいなかった。

では解決してみよう。

main.cpp
#include "func.hpp"
#define hoge 1
int main()
{
    func1(3);
    return 0;
}

めでたい!解決である。

セキュリティ上の問題

JPCERTのページにこんなのがある。

関数プロトタイプでは引数に名前をつけない

なるほど、場合によってはセキュリティ上のリスクになるのか、それは気が付かなかった。

ということで、普段コードを書くときは #define は後ろに、ヘッダファイルを書くときは引数名は書かないように、ということなのだろう。

2
1
7

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?