【注意】下記はC言語での話であって、C++言語の話ではありません。混同しないように!
仕様書を確認する
C11仕様書最終ドラフトn1570には
int main(void) { /* ... */ }
int main(int argc, char *argv[]) { /* ... */ }
の二つだけしか書いていません。また、char *argv[]
の部分はchar **argv
としてもよいと備考に書かれています。なお、argc
とargv
は別の名前でもかまいません。ただし、これ以外を禁止しているのでは無く、実装定義の方法でもよいと書いてあります。
では、どうすべきでしょうか?OSやコンパイラによっていちいちmain
の書き方を変えるのは、移植性を低くし、不具合の温床とのなります。よって、上の三種類以外は使用すべきではありません。上の三つが正常に動作しないようなコンパイラは消費期限が切れてますので 捨てて下さい 。
結論
main
を定義するときはint main(void) {}
、int main(int argc, char *argv[]) {}
、int main(int argc, char **argv) {}
を使いなさい。
完
ちゃんと確認する
これで終わってしまっては駄目なので、巷の噂にある物を確認してみます。コンパイルはclang -Wall -std=c11 ${name}.c -o ${name}
でしてみました。
$ clang --version
Apple LLVM version 6.0 (clang-600.0.56) (based on LLVM 3.5svn)
Target: x86_64-apple-darwin14.1.0
Thread model: posix
検証1 戻り値の型を省略するとint
になるので、書かなくてもよい。
省略できるなら、書かなくてもいいよね?
main(void)
{
return 0;
}
$ clang -Wall -std=c11 none_main_void.c -o none_main_void
none_main_void.c:1:1: warning: type specifier missing, defaults to 'int'
[-Wimplicit-int]
main(void)
^~~~
1 warning generated.
暗黙のint
が使われますという警告が表示されますが、一応はコンパイルできました。これはC言語の古い書き方の互換性のために残されている文法です。将来廃止される可能性があり、使うべきではありません。
検証2 戻り値の型はvoid
でもよい。
ふるーいC言語入門によくある書き方です。
void main(void)
{
return;
}
$ clang -Wall -std=c11 void_main_void.c -o void_main_void
void_main_void.c:1:1: error: 'main' must return 'int'
void main(void)
^~~~
int
1 error generated.
戻り値の型がint
じゃないとエラーになってコンパイルに失敗しました。void
でもよい実装のコンパイラなら通るかもしれませんが、実装依存になってしまいますので、使わないようにしましょう。
検証3 ()
とすると(...)
と解釈される。
そもそも(...)
って何でしょうか?可変長パラメーターの事だとするとva_start
の第2パラメーターはどうするでしょうかね?
int main()
{
return 0;
}
$ clang -Wall -std=c11 int_main_empty.c -o int_main_empty
エラーも何も無しでコンパイルできました。int main(void) {}
と逆アセンブリして比較すると全く同じになっています(int main(int argc, char *argv[])
は異なります)。あれ、結局、C++と一緒でint main() {}
でいいんじゃ無いのか?
いえいえ、そうでありません。C++言語では(void)
と同じですが、C言語の()
はまた別の意味になります。次のコードを見て下さい。
int a();
int main(void)
{
a();
a(1);
a(1, 2);
return 0;
}
int a()
{
return 0;
}
a()
関数にパラメーターを色々渡していますが、エラーになりません。もしint a(void)
としていた場合は、a(1)
のところでエラーになってしまいます。つまり、()
はどんな引数でもかまわないという意味になります。なお、va_start
の第2パラメーターに指定する変数が無いので、0個以上の可変長引数としては使えません。そもそも可変長引数は, ...
という表記を最後に付けるという定義なので(...)
というものはありません。
なお、仕様書には空の()
は廃止予定と記載されていますので、使うべきではありません。
検証4 環境変数が入った第3のパラメーターがあるらしい。
第3の男、現る。
int main(int argc, char *argv[], char *envp[])
{
return 0;
}
$ clang -Wall -std=c11 int_main_envp.c -o int_main_envp
問題なくコンパイルできます。実は、仕様書の付録Jにこのenvp
に関する記載があります。envp
は環境変数が入っている文字列の配列で、一番最後がNULL
になっています。付録に記載されていることもあり、ほとんどの環境で同じように使えるようです。ただ、実装依存であり、全ての環境で使えることが保証されていないため、使用しない方がいいでしょう。環境変数はgetenv()
等で操作をして下さい。
結論
main() {}
でも動くけど、ある日突然エラーになっても知らないからね。