最初の例では、最初の ANSI(1989) および ISO(1990) 標準より前の C の時代遅れの方言から継承された機能を使用しています。つまり、戻り値の型を指定しない関数を記述でき、その場合、タイプのデフォルトはint
です。
初期の C では、void
キーワードと関連する型は存在しませんでした。プログラマーがプロシージャー (「副作用はあるが何も返さない関数」) を書きたい場合、この機能を使用してシミュレートしました。彼らは、戻り値の型を指定するキーワードなしで関数を書きました。彼らは、関数が値を返さずに最後のステートメントまで実行できるようにし (または、値return;
を提供せずに途中で終了していました)、それらの呼び出しが return を使用しようとしないように関数の呼び出しを記述しました。価値:
parse_input() /* similar to a procedure in Pascal, but fake! */
{
/* ... */
if (condition())
return; /* no value */
/* ... */
/* fall off end here */
}
int main()
{
parse_input(); /* no return value extracted, everything cool! */
return 0;
}
残念ながら、一部のプログラマーは、プログラムの終了ステータスを気にせずmain
、この手順スタイルで自分自身を書き始めました。
main()
{
/* do something */
/* fall off the end without returning a value */
}
int
(宣言子を省略して整数値を返す混合スタイルも存在しました。)
値を返さないこれらのプログラムは、不確定な終了ステータスを持っていました。オペレーティング システムには、実行が成功したか失敗したかのように見えます。このようなプログラムの終了ステータスに依存しようとしたスクリプト ライターは悲惨です。
その後、事態は悪化しました。C++ が登場して導入されvoid
、C に採用されましvoid
た。C++ のキーワードを使用すると、実際には何も返さない関数を宣言できます (return;
他の種類の関数にステートメントがあるとエラーになります)。戻り値の型なしで書いmain
ていたダミーのプログラマーは頭がおかしくなり、この新しい、C++ から抜け出したばかりの新しいものを前面に貼り付け始めましvoid
た。
void main() /* yikes! */
{
/* do something */
/* fall off the end without returning a value */
}
この時までに、彼らは を書いたとき、main()
実際には を意味していたことを忘れていましたint main()
。これにより、関数は、環境によって呼び出されるスタートアップ呼び出しと互換性のある型になりました (値を返すことを無視するという問題を除いて)。実際には、期待されたものとは異なる関数型があり、正常に呼び出されない可能性があります!
現在のところ、C++ および最新の C++ 標準でmain
は、int
. しかし、どちらの言語も元のダミー プログラマーに譲歩します。つまり、実行を最後に "フォールオフ" させることができmain
、動作はあたかもreturn 0;
そこで実行されたかのようになります。したがって、この簡単なプログラムは、C99 および C++98 (またはそれ以前) の時点で正常終了ステータスになりました。
int main()
{
}
しかし、どちらの言語も、第 2 世代の愚かなプログラマー (および、それらのプログラマーが 1980 年代以降に書いた C の本を読んでいるすべての人) に譲歩するものではありません。つまりvoid
、有効な return 宣言子ではありませんmain
(ただし、プラットフォームによって受け入れられると文書化されており、移植可能な言語ではなく、それらのプラットフォームにのみ適用される場合を除きます)。
ああ、欠落している宣言子の許可は C99 で C から削除されたため、main() { }
C の新しい方言では正しくなく、有効な C++ ではありません。ちなみに、C++ には他の場所でもそのような構文があります。つまり、クラス コンストラクターとデストラクターは戻り値の型指定子を持たない必要があります。
さて、今度は vs について()
です(void)
。C++ が を導入したことを思い出してくださいvoid
。さらに、C++には が導入されていますが、引数の構文void
は導入されていません。(void)
C++ はより厳格に型付けされ、プロトタイプ宣言を導入し、プロトタイプ化されていない関数の概念を追放しました。C++ は()
C 構文の意味を変更して、宣言する力を与えました。C++ では、int func();
引数なしで関数を宣言しますが、C ではint func();
そのようなことはしません。引数情報がわからない関数を宣言します。C が を採用したときvoid
、委員会は醜い考えを持っていました: 構文を使用して(void)
引数なしで関数を宣言してから、()
構文は、型のないプログラミングに迎合するゆるいグーシーなレガシー動作との下位互換性を維持できます。
次に何が起こったかは推測できます。C++ の人々はこの(void)
ハックを見て、腕を上げて、言語間の互換性のために C++ にコピーしました。後から考えると、言語が今日どのように分岐しているかを見ると驚くべきことであり、基本的にその程度の互換性はもはや気にしません. したがって(void)
、C と C++ の両方で、明確に「引数なしとして宣言する」ことを意味します。しかし、明らかに純粋な C++ であり、C であることを決して意図していない C++ コードでそれを使用するのは、醜く、スタイルが悪い: たとえば、クラス メンバー関数で! みたいなことを書いてもあまり意味がないclass Foo { public: Foo(void); virtual ~Foo(void) /*...*/ };
もちろん、のような関数を定義するint main() { ... }
と、定義された関数は、それがどの言語であるかに関係なく、引数を持ちません。違いは、どの宣言情報がスコープに導入されるかです。C では、プログラム テキストの同じ単位で、関数を完全に定義できるのに宣言しないというばかげた状況が発生する可能性があります。
を書くときmain
、通常、それはプログラム内から呼び出されるわけではないので、定義が何を宣言しているかは問題ではありません。(C++ では、main
プログラムから呼び出してはなりません。C では呼び出すことができます)。したがって、C を使用しているか C++ を使用しているかに関係なく、int main()
またはを書くかどうかは重要ではありません。int main(void)
呼び出すものmain
は、それの宣言を見ません(とにかく、プログラムに書きます)。
したがって、次のように書く場合は、次のことに注意してください。
int main() /* rather than main(void) */
{
}
次に、これは完全な C++ であり正しい C ですが、C としては文体にわずかな欠陥があります。つまり、プロトタイプとして機能しない古いスタイルの ANSI-C より前の関数を作成しています。の場合は機能的には問題ありませんがmain
、一部のコンパイラを特定の方法で使用すると、警告が表示される場合があります。たとえば、GCC では次の-Wstrict-prototypes
オプションを指定します。
test.c:1:5: warning: function declaration isn’t a prototype [-Wstrict-prototypes]
は、C でプログラミングするときにオンにすると非常に便利な警告であり、型の安全性を向上させるため-Wstrict-prototypes
に (とともに-Wmissing-prototypes
)、コンパイル ジョブから警告を排除するよう努めているため、次のように記述する必要があります。
int main(void) /* modern C definition which prototypes the function */
{
}
これにより、その診断が消えます。
main
引数を受け入れたい場合はint main(int argc, char **argv)
、ここでパラメーター名を指定します。
C++ では、パラメーター名を省略できるため、この定義が可能であり、main()
.
int main(int, char **) // both arguments ignored: C++ only
{
}
引数ベクトルは null ポインターで終了するため、 は必要ありませんargc
。C++ では、未使用の変数を導入せずにそれを表現できます。
#include <cstdio>
int main(int, char **argv) // omitted param name: C++ only
{
// dump the arguments
while (*argv)
std::puts(*argv++);
}