1. ホーム
  2. c++

[解決済み] main関数の代わりにmainというグローバル変数があるプログラムはどのように動作するのでしょうか?

2023-01-26 10:50:56

質問内容

次のようなプログラムを考えてみましょう。

#include <iostream>
int main = ( std::cout << "C++ is excellent!\n", 195 ); 

Windows 7 OS 上で g++ 4.8.1 (mingw64) を使用すると、プログラムは正常にコンパイルされ、印刷も実行されます。

C++は素晴らしいです!

をコンソールに表示します。 main は関数ではなく、グローバル変数のように見えます。 main() ? このコードはC++の標準に準拠していますか?プログラムの動作はきちんと定義されていますか?また、私は -pedantic-errors オプションも使用しましたが、プログラムはまだコンパイルされ、実行されます。

どのように解決するのですか?

何が起こっているかという質問の本題に入る前に、プログラムが以下のように不正な形式であることを指摘することが重要です。 不具合報告 1886 にあるように、プログラムが不正な形式であることを指摘することが重要です。main() のための言語リンク :

[...] グローバルスコープで変数mainを宣言したり、C言語のリンクで(任意の名前空間で)mainという名前を宣言しているプログラムは不正な形式である。[...]

clangとgccの最新バージョンでは、これをエラーとし、プログラムはコンパイルされません( gcc の実例を見る ):

error: cannot declare '::main' to be a global variable
int main = ( std::cout << "C++ is excellent!\n", 195 ); 
    ^

では、なぜ古いバージョンのgccやclangでは診断がなかったのでしょうか。この不具合報告は2014年後半まで解決案すら出ていなかったので、このケースはごく最近になって明示的に診断が必要とされるようになったのです。

これに先立ち、これは、私たちが違反をしているので、未定義の動作になるようです。 とする

セクションの C++ 標準草案の要件に違反しているためです。 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 であり、これは である。 グローバル名前空間で宣言され、初期化されている場合、その変数は静的な保存期間を持つ。を呼び出そうとする前に初期化が行われるかどうかは、実装で定義されています。 main を呼び出す前に初期化を行うかどうかは実装で定義されていますが、 gcc ではこの初期化を行った後に main .

このコードでは カンマ演算子 を呼び出すための副次的な効果としてのみ使用されています。 std::cout . コンマ演算子の結果は右オペランドで、この場合は prvalue です。 195 であり、これが変数 main .

私たちが見ることができるのは が指摘するように は、生成されたアセンブリを見ると cout が静的初期化中に呼び出されていることがわかります。しかし、議論する上でより興味深い点は ゴッドボルトのライブセッションを見る はこれでしょう。

main:
.zero   4

と続く。

movl    $195, main(%rip)

考えられるシナリオは、プログラムがシンボル main という記号にジャンプし、そこに有効なコードがあることを期待し でセグメンテーションエラーになる場合があります。 . もしそうなら、私たちは有効なマシンコードを変数 main 動作可能なプログラム のように、コード実行が可能なセグメントに位置していると仮定します。私たちが見ることができるのは この1984年のIOCCCのエントリ ただそれだけ .

C言語では、gccにこれをさせることができるようです( ライブを見る ):

const int main = 195 ;

これは、もし変数 main が const でない場合、segment fault が発生します。これはおそらく実行可能な場所にないためと思われます。 コメント にHat Tipして、このアイデアを得ました。

また FUZxxlの回答はこちら のC言語版もご覧ください。