何が起こっているのかという問題の詳細に入る前に、プログラムが欠陥レポート1886:main()の言語リンケージに従って不正な形式であることを指摘することが重要です。
[...]グローバルスコープで変数mainを宣言するプログラム、または(任意の名前空間で)C言語リンケージを使用して名前mainを宣言するプログラムは、不正な形式です。[...]
clangとgccの最新バージョンでは、これがエラーとなり、プログラムはコンパイルされません(gcc liveの例を参照)。
error: cannot declare '::main' to be a global variable
int main = ( std::cout << "C++ is excellent!\n", 195 );
^
では、なぜ古いバージョンのgccとclangに診断がないのですか?この欠陥レポートには2014年後半まで解決策が提案されていなかったため、このケースはごく最近明確に不正であり、診断が必要です。
これより前は、セクション[basic.start.main]の C ++標準ドラフトのshall要件に違反しているため、これは未定義の動作のよう3.6.1
です。
プログラムには、プログラムの指定された開始点であるmainと呼ばれるグローバル関数が含まれます。[...]
未定義の動作は予測不可能であり、診断を必要としません。動作の再現で見られる不整合は、典型的な未定義の動作です。
それでは、コードは実際に何をしており、なぜ場合によっては結果を生成するのでしょうか?私たちが持っているものを見てみましょう:
declarator
| initializer----------------------------------
| | |
v v v
int main = ( std::cout << "C++ is excellent!\n", 195 );
^ ^ ^
| | |
| | comma operator
| primary expression
global variable of type int
我々が持っているmain
であるint型グローバル名前空間で宣言され、初期化され、変数は静的記憶域期間を持ちます。呼び出しの試行が行われる前に初期化が行われるかどうかは、実装によって定義されますmain
が、gccは呼び出しの前に行われるようmain
です。
このコードではカンマ演算子を使用しており、左のオペランドは破棄された値の式であり、ここでは呼び出しの副作用のためにのみ使用されますstd::cout
。コンマ演算子の結果は、右側のオペランド195
ですmain
。この場合、変数に割り当てられているprvalue です。
sergejがcout
静的な初期化中に呼び出される生成されたアセンブリショーを指摘しているのがわかります。議論のためのより興味深いポイントがライブゴッドボルトセッションを参照してくださいがこれは次のようになります:
main:
.zero 4
以降:
movl $195, main(%rip)
考えられるシナリオは、プログラムがmain
有効なコードが存在することを期待してシンボルにジャンプし、場合によってはseg-faultになることです。そのmain
ため、コードの実行を許可するセグメント内にいると仮定すると、変数に有効なマシンコードを格納すると、実行可能なプログラムにつながる可能性があると予想されます。この1984年のIOCCCエントリはまさにそれを行っていることがわかります。
これを使用してCでgccにこれを実行させることができるようです(実際に見る):
const int main = 195 ;
変数main
が実行可能な場所に配置されていないために変数がconstでない場合、セグメンテーションフォールトが発生します。このコメントへのハットヒント。
また、この質問のC固有のバージョンに対するFUZxxlの回答も参照してください。