main()は本当にC ++プログラムの始まりですか?


131

C ++標準のセクション$ 3.6.1 / 1は、

プログラムには、プログラムの指定された開始点であるmainと呼ばれるグローバル関数が含まれます。

今、このコードを考えて、

int square(int i) { return i*i; }
int user_main()
{ 
    for ( int i = 0 ; i < 10 ; ++i )
           std::cout << square(i) << endl;
    return 0;
}
int main_ret= user_main();
int main() 
{
        return main_ret;
}

このサンプルコードは、私が意図することを実行します。つまり、プログラムの「開始」であるはずの関数入るに、0から9の整数の2乗を出力しますmain()

また-pedantic、GCC 4.5.0オプションでコンパイルしました。エラーも警告もありません!

だから私の質問は、

このコードは本当に標準に準拠していますか?

それが標準に準拠している場合、それは標準が言うことを無効にしないのですか?main()このプログラムの開始ではありません!user_main()の前に実行されmain()ます。

グローバル変数を初期化するには、最初main_retにをuse_main()実行しますが、それはまったく別のことです。ポイントは、そのあるとして、$ 3.6.1 / 1標準からの引用文を無効にmain()しないで起動プログラムの。それは実際には最後このプログラム!


編集:

「スタート」という言葉をどのように定義しますか?

これは、「プログラムの開始」というフレーズの定義に要約されます。それでは、どのように正確に定義しますか?

回答:


85

いいえ、C ++はmainを呼び出す前に「環境を設定する」ために多くのことを行います。ただし、mainは、C ++プログラムの「ユーザー指定」部分の正式な開始です。

一部の環境設定は制御できません(std :: coutを設定する初期コードなど)。ただし、一部の環境は、静的グローバルブロック(静的グローバル変数を初期化するため)のように制御できます。完全なmainの前の制御では、静的ブロックが初期化される順序を完全に制御することはできません。

メインの後、コードは概念的にプログラムを「完全に制御」します。つまり、実行する命令と実行する順序の両方を指定できるという意味です。マルチスレッドは、コードの実行順序を並べ替えることができます。ただし、コードのセクションを(場合によっては)順序どおりに実行しないように指定したため、C ++は引き続き制御できます。


9
このための+1 「メインの前に完全な制御ができないため、静的ブロックが初期化される順序を完全に制御できないことに注意してください。メインの後、コードは概念的に「完全に制御」されます実行する命令とそれらを実行する順序の両方を指定できるという意味で、プログラムこれにより、この回答を承認済みの回答としてマークすることもできます ...これらは「プログラムの開始」main()として十分に正当化できる非常に重要なポイントだと思います
Nawaz

13
@Nawaz:初期化順序を完全に制御できない上に、初期化エラーを制御できないことに注意してください。グローバルスコープで例外をキャッチすることはできません。
アンドレ・キャノン

@Nawaz:静的グローバルブロックとは何ですか?簡単な例で説明していただけますか?ありがとう
デストラクタ

@meet:名前空間レベルで宣言されたオブジェクトにはstatic保存期間があり、異なる翻訳単位に属するこれらのオブジェクトは任意の順序で初期化できます(順序は標準で指定されていないため)。それがあなたの質問に答えるかどうかはわかりませんが、それはこのトピックの文脈で言えることです。
Nawaz

88

文章を間違って読んでいます。

プログラムには、プログラムの指定された開始点である mainと呼ばれるグローバル関数が含まれます

規格は、規格の残りの目的のために「開始」という言葉を定義しています。main呼び出される前にコードが実行されないということではありません。これは、プログラムの開始が関数であると見なされていることを示していますmain

プログラムは準拠しています。mainが開始されるまで、プログラムは「開始」されません。コンストラクタは、標準の「開始」の定義に従ってプログラムが「開始」する前に呼び出されますが、それはほとんど問題ではありません。コードの多くは、前に実行さmain、これまでだけではなく、この例では、すべてのプログラムで呼ばれます。

説明のために、コンストラクターコードはプログラムの「開始」の前に実行され、標準に完全に準拠しています。


3
申し訳ありませんが、私はその条項のあなたの解釈に同意しません。
オービットの軽さのレース

Adam Davisは正しいと思います。「メイン」は、ある種のコーディング制限のようなものです。
laike9m 2013

@LightnessRacesinOrbit私はフォローアップしませんでしたが、その文は論理的に「メインと呼ばれるグローバル関数がプログラムの指定された開始点である」に論理的に要約できます(強調が追加されています)。その文のあなたの解釈は何ですか?
Adam Davis

1
@AdamDavis:私の懸念が何であったか覚えていません。今は考えられない。
オービットのライトネスレース

23

メインがないと、プログラムはリンクせず、実行されません。ただし、ファイルレベルのオブジェクトには事前に実行されるコンストラクターがあり、main()に到達する前にその存続期間を実行するプログラム全体を記述してmain自体に実行させることができるため、main()はプログラムの実行の開始を引き起こしません空の体。

実際にこれを実施するには、プログラムのすべてのフローを呼び出すために、mainとそのコンストラクターの前に作成される1つのオブジェクトが必要です。

これを見てください:

class Foo
{
public:
   Foo();

 // other stuff
};

Foo foo;

int main()
{
}

プログラムの流れは、 Foo::Foo()


13
+1。ただし、異なる翻訳単位に複数のグローバルオブジェクトがある場合、コンストラクターが呼び出される順序が定義されていないため、問題がすぐに発生することに注意してください。シングルトンと遅延初期化で済むことができますが、マルチスレッド環境では、物事はすぐに醜くなります。つまり、実際のコードではこれを行わないでください。
Alexandre C.

3
おそらくmain()にコード内の適切な本体を与え、実行を許可する必要がありますが、起動の外側にあるオブジェクトの概念は、多くのLD_PRELOADライブラリが基づいているものです。
CashCow 2011年

2
@アレックス:規格は未定義ですが、実際問題として、リンク順(通常はコンパイラによって異なります)は開始順を制御します。
ThomasMcLeod

1
@トーマス:私はきっとそれにリモートで依存しようとさえしないでしょう。また、ビルドシステムを手動で制御しようとはしません。
Alexandre C.

1
@Alex:それほど重要ではありませんが、物理メモリのページングを減らすために、リンクオーダーを使用してビルドイメージを制御していました。起動時のパフォーマンス比較テストなど、プログラムのセマンティクスに影響を与えない場合でも、初期化の順序を制御する必要があるかもしれない他の理由があります。
ThomasMcLeod 2011年

15

あなたも質問に「C」というタグを付けました。厳密にCについて言えば、ISO C99標準のセクション6.7.8「初期化」に従って初期化は失敗するはずです。

この場合に最も関連するのは、制約4で、

静的な保存期間を持つオブジェクトの初期化子のすべての式は、定数式または文字列リテラルでなければなりません。

したがって、あなたの質問に対する答えは、コードがC標準に準拠していないということです。

C ++標準のみに関心がある場合は、「C」タグを削除することをお勧めします。


4
@ Remo.Dでそのセクションの内容を教えてください。私たち全員がC標準を持っているわけではありません:)。
UmmaGumma 2011年

2
あなたはとてもうるさいので、悲しいかな、ANSI Cは1989年以来廃止されています。ISOC90またはC99は引用する関連規格です。
ランディン、2011年

@Lundin:誰も十分にうるさいという人はいません:)私はISO C99を読んでいましたが、C90にも当てはまると確信しています。
Remo.D 2011年

@一発。あなたが正しい、私がここで最も関連があると思う文を追加しました。
Remo.D、2011年

3
@Remo:有効ではないという情報を提供するための+1 C; 知らなかった。これは、人々が、時には計画によって、時には偶然に学ぶ方法です!
Nawaz、2011年

10

セクション3.6は全体として、main動的初期化の相互作用について非常に明確です。「プログラムの指定された開始」は他では使用されず、の一般的な意図を説明するだけですmain()。この1つのフレーズを標準のより詳細で明確な要件に反する規範的な方法で解釈しても意味がありません。


9

多くの場合、コンパイラーは、標準に準拠するためにmain()の前にコードを追加する必要があります。規格では、プログラムの実行にグローバル/静的の初期化を行う必要があると規定されているためです。また、前述のとおり、ファイルスコープ(グローバル)に配置されたオブジェクトのコンストラクターにも同じことが言えます。

したがって、元の質問 Cにも当てはまります。Cプログラムでは、プログラムを開始する前にグローバル/静的初期化を行う必要があるためです。

標準は、これらの変数がプログラムの初期化前にどのように設定されるべきを述べていないため、これらの変数は「マジック」によって初期化されることを前提としています。彼らはそれをプログラミング言語標準の範囲外のものとして考えたと思います。

編集:たとえば、ISO 9899:1999 5.1.2を参照してください。

静的な保存期間を持つすべてのオブジェクトは、プログラムの起動前に初期化(初期値に設定)されます。そうでなければ、そのような初期化の方法とタイミングは特定されていません。

この「魔法」が行われる方法の背後にある理論は、RAMベースのコンピューターでUNIX OSのみで使用することを目的としたプログラミング言語であったCの誕生にさかのぼります。理論的には、プログラムは、プログラム自体がRAMにアップロードされると同時に、実行可能ファイルからすべての初期化済みデータをRAMにロードできます。

それ以来、コンピュータとOSは進化し、Cは当初の予想よりもはるかに広い領域で使用されています。最近のPC OSには仮想アドレスなどがあり、すべての組み込みシステムはRAMではなくROMからコードを実行します。したがって、RAMを「自動的に」設定できない状況がたくさんあります。

また、この標準はあまりにも抽象的すぎて、スタックやプロセスメモリなどについて知ることができません。これらのことも、プログラムを開始する前に行う必要があります。

したがって、ほとんどすべてのC / C ++プログラムには、標準の初期化規則に準拠するために、mainが呼び出される前に実行されるinit / "copy-down"コードがあります。

例として、組み込みシステムには通常、「非ISO準拠の起動」と呼ばれるオプションがあり、パフォーマンス上の理由から初期化フェーズ全体がスキップされ、コードは実際にはメインから直接開始されます。ただし、グローバル/静的変数の初期値に依存できないため、このようなシステムは標準に準拠していません。


4

「プログラム」は、単にグローバル変数から値を返します。それ以外はすべて初期化コードです。したがって、標準が成り立つ-あなたは非常に簡単なプログラムとより複雑な初期化を持っているだけです。



2

英語のセマンティクスの問題のようです。OPは、最初に彼のコードブロックを「コード」と呼び、後に「プログラム」と呼びます。ユーザーがコードを記述してから、コンパイラーがプログラムを記述します。


1

mainは、すべてのグローバル変数を初期化した後に呼び出されます。

標準で指定されていないのは、すべてのモジュールおよび静的にリンクされたライブラリのすべてのグローバル変数の初期化の順序です。


0

はい、メインは、実装固有の拡張機能を除いて、すべてのC ++プログラムの「入り口」です。それでも、main_retのようなグローバル初期化など、いくつかのことがmainの前に発生します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.