「int main」です。有効なC / C ++プログラム?


113

私のコンパイラはそうではないのに、コンパイラがそう思っているように見えるので、私は尋ねます。

echo 'int main;' | cc -x c - -Wall
echo 'int main;' | c++ -x c++ - -Wall

Clangはこれに関する警告やエラーを発行せず、gccは微妙な警告のみを発行します'main' is usually a function [-Wmain]が、Cとしてコンパイルされた場合のみ-std=です。aの指定は重要ではないようです。

それ以外の場合は、コンパイルして正常にリンクします。しかし、実行すると、SIGBUS(私にとっては)ですぐに終了します。

CとC ++でmain()が何を返す必要があるか(優れた)回答を読む そして、言語仕様をざっと見てみると、メインの機能が必要なように思えます。しかし、gcc ( 'main'は通常は関数です)からの言い回し(およびここでのエラーの不足)は、おそらく別のことを示唆しているようです。-Wmain

しかし、なぜ?これには、奇妙なエッジケースまたは「歴史的」な使用がありますか?誰が何を与えるか知っていますか?

私のポイントは、おそらく、これはホストされた環境ではエラーになるはずだと私は本当に思っているということです。


6
あなたが必要とするGCC(ほとんど)規格に準拠コンパイラ作成するにはgcc -std=c99 -pedantic ...
PMG

3
有無にかかわらず、その同じ警告を@pmg -pedanticまたは任意の-std。私のシステムc99も警告やエラーなしでこれをコンパイルします...
Geoff Nixon

3
残念ながら、あなたが「十分に賢い」ならば、コンパイラーによって受け入れられるが意味をなさないものを作成することができます。この場合、Cランタイムライブラリをリンクしてと呼ばれる変数を呼び出しますがmain、これは機能しそうにありません。mainを「正しい」値で初期化すると、実際に返される可能性があります...
Mats Petersson 2015年

7
そして、それが有効であるとしても、それを行うのはひどいことです(判読不能なコード)。ところで、それはホストされた実装と(それについて知らないmain)独立した実装では異なる可能性があります
Basile Starynkevitch

1
もっと楽しい時間をお試しくださいmain=195;
イマレット

回答:


97

質問にはCとC ++の二重タグが付いているため、C ++とCの理由は異なります。

  • C ++は名前のマングリングを使用して、リンカーが異なるタイプのテキスト上同一のシンボルを区別できるようにします(例えば、グローバル変数xyzと独立したグローバル関数)xyz(int)。ただし、名前mainが壊されることはありません。
  • Cはマングリングを使用しないため、プログラムは、あるシンボルを別のシンボルの代わりに提供することによってリンカーを混乱させ、プログラムを正常にリンクさせることができます。

それがここで起こっていることです:リンカはシンボルを見つけることを期待しmain、それを行います。それはそれがよりよく知らないので、それが関数であるかのようにそのシンボルを「配線」します。制御を渡すランタイムライブラリの部分mainはリンカーにを要求するためmain、リンカーはそれにシンボルを与えmain、リンクフェーズを完了させます。もちろん、これmainは関数ではないため、実行時に失敗します。

次に、同じ問題の別の図を示します。

ファイルxc:

#include <stdio.h>
int foo(); // <<== main() expects this
int main(){
    printf("%p\n", (void*)&foo);
    return 0;
}

ファイルyc:

int foo; // <<== external definition supplies a symbol of a wrong kind

コンパイル:

gcc x.c y.c

これはコンパイルされ、おそらく実行されますが、コンパイラーに約束されたシンボルのタイプがリンカーに提供された実際のシンボルと異なるため、未定義の動作です。

警告に関する限り、私はそれは妥当だと思います。Cではmain関数を持たないライブラリを構築できるため、何らかの未知の理由でmain変数を定義する必要がある場合、コンパイラは他の用途のために名前を解放しますmain


3
ただし、C ++コンパイラではメイン関数の扱いが異なります。その名前はextern "C"がなくても壊されません。そうでない場合は、リンクを確実にするために、独自の外部「C」メインを発行する必要があるためです。
UldisK、2015年

@UldisKはい、私自身もこれに気づき、非常に興味深いと思いました。それは理にかなっているが、私はそれについて考えたことはなかった。
ジェフニクソン、

2
実際、ここで指摘したように、C ++とCの結果同じです— main関数であるかどうかに関係なく、C ++では名前のマングリング(そう思われる)の影響を受けません。
ジェフニクソン、

4
@nm私はあなたの質問の解釈が狭すぎると思います:投稿のタイトルで質問をすることに加えて、OPは明らかに彼のプログラムが最初にコンパイルされた理由の説明を明らかに求めます(「私のコンパイラはそう思うようです、私はしていません」)と同様にmain、関数以外のものとして定義することが有用である理由の提案も同様です。答えは両方の部分の説明です。
dasblinkenlight 2015年

1
シンボルmainが名前のマングリングの影響を受けないということは無関係です。C ++標準では名前のマングリングについての言及はありません。名前のマングリングは実装の問題です。
David Hammen、2015年

30

mainない予約語それだけだ定義済みの識別子(のようなcinendlnposあなたはという変数を宣言することができるように、...) main、それを初期化し、その値をプリントアウトします。

もちろん:

  • これはかなりエラーを起こしやすいので警告は役に立ちます。
  • main()関数なしでソースファイルを作成できます(ライブラリ)。

編集

いくつかの参照:

  • main 予約語ではありません(C ++ 11):

    関数mainはプログラム内で使用されません。のリンケージ(3.5)mainは実装定義です。削除またはそのされるメイン宣言としてメイン規定したプログラムinlinestaticまたは constexpr悪い形成されています。それ以外の場合、名前mainは予約されません。[例:main他の名前空間のエンティティと同様に、メンバー関数、クラス、列挙を呼び出すことができます。—例を終了]

    C ++ 11-[basic.start.main] 3.6.1.3

    [2.11 / 3] [...]一部の識別子は、C ++実装および標準ライブラリ(17.6.4.3.2)で使用するために予約されており、それ以外では使用されません。診断は必要ありません。

    [17.6.4.3.2 / 1]特定の名前と関数シグネチャのセットは、常に実装に予約されています。

    • 二重下線__を含む名前、またはアンダースコアで始まり、その後に大文字(2.12)が続く名前は、実装用に予約されています。
    • アンダースコアで始まる各名前は、グローバル名前空間の名前として使用するために実装で予約されています。
  • プログラミング言語の予約語

    予約語はプログラマーが再定義することはできませんが、事前定義されたものは、多くの場合、オーバーライドできます。これは次の場合ですmain。その識別子を使用する宣言がその意味を再定義するスコープがあります。


-私(これエラーが発生しやすいので)なぜこれが警告(エラーではない)であり、なぜCとしてコンパイルしたときに警告にすぎないのかということに誤解していると思います-確かに、なしでコンパイルできますmain()この関数は、しかし、あなたはそれをプログラムとしてリンクすることはできません。何ここで起こっていることは、「有効」プログラムをせずにリンクされていることでmain()、ちょうどmain
ジェフニクソン、

7
cinそして、endlデフォルトの名前空間ではありません-彼らはしているstd名前空間。nposはのメンバーですstd::basic_string
AnotherParker 2015年

1
main されるグローバル名として予約。あなたが述べた他のこともmain、も事前定義されていません。
Potatoswatter、2015年

1
main許可されている制限については、C ++ 14§3.6.1およびC11§5.1.2.2.1を参照してください。C ++は「実装はメイン関数を事前定義してはならない」と述べ、Cは「実装はこの関数のプロトタイプを宣言しない」と述べています。
Potatoswatter、2015年

@manlio:引用元を明確にしてください。明白なCに関しては、引用は間違っています。だから私はそれがc ++標準のどれかだと思いませんか?
2015年

19

あるint main;有効なC / C ++プログラムは?

C / C ++プログラムが何であるかは完全には明らかではありません。

あるint main;有効なCプログラムは?

はい。独立した実装は、そのようなプログラムを受け入れることが許可されています。main自立した環境では、特別な意味を持つ必要はありません。

それはないホスト環境で有効。

あるint main;有効なC ++プログラムは?

同上。

なぜクラッシュするのですか?

プログラムはあなたの環境で意味をなす必要はありません。独立した環境では、プログラムの起動と終了、およびの意味mainは実装定義です。

コンパイラが警告するのはなぜですか?

コンパイラーは、適合プログラムを拒否しない限り、何であれ警告を表示する場合があります。一方、警告は、不適合プログラムを診断するために必要なすべてです。この変換単位を有効なホストプログラムの一部にすることはできないため、診断メッセージが正当化されます。

あるgcc自立した環境は、またはそれがホスト環境とは?

はい。

gcc-ffreestandingコンパイルフラグを文書化します。それを追加すれば、警告は消えます。カーネルやファームウェアなどをビルドするときに使用したいかもしれません。

g++そのようなフラグは文書化されていません。提供してもこのプログラムには影響がないようです。g ++によって提供される環境がホストされていると想定しても、おそらく安全です。この場合の診断の欠如はバグです。


17

技術的に禁止されていないため、警告です。スタートアップコードは、「main」のシンボル位置を使用し、3つの標準引数(argc、argv、envp)でそこにジャンプします。そうではなく、リンク時に、それが実際に関数であることも、それらの引数があることも確認できません。これは、int main(int argc、char ** argv)が機能する理由でもあります。コンパイラはenvp引数を認識せず、たまたま使用されないだけで、呼び出し側のクリーンアップです。

冗談として、次のようなことができます

int main = 0xCBCBCBCB;

x86マシンでは、警告などを無視すると、コンパイルだけでなく実際にも機能します。

誰かがこれと同様の手法を使用して、複数のアーキテクチャで直接実行される実行可能ファイル(一種)を作成しました-http://phrack.org/issues/57/17.html#article。IOCCC- http: //www.ioccc.org/1984/mullender/mullender.cを勝ち取るためにも使用されました。


1
「技術的に許可されていないため、警告です」-C ++では無効です。
乾杯とhth。-Alf

3
「3つの標準引数(argc、argv、envp)」-ここでは、Posix標準について話している可能性があります。
乾杯とhth。-Alf

私のシステム(Ubuntu 14 / x64)では、次の行がgccで機能しますint main __attribute__ ((section (".text")))= 0xC3C3C3C3;
。– csharpfolk

@ Cheersandhth.-Alf最初の2つは標準、3つ目はPOSIXです。
dascandy 2015年

9

それは有効なプログラムですか?

番号。

実行可能な部分がないため、プログラムではありません。

コンパイルは有効ですか?

はい。

有効なプログラムで使用できますか?

はい。

すべてのコンパイル済みコードが有効であるために実行可能である必要はありません。例としては、静的ライブラリと動的ライブラリがあります。

これで、オブジェクトファイルが効果的に作成されました。これは有効な実行可能ファイルではありませんが、実行main時にロードすることにより、別のプログラムが結果ファイル内のオブジェクトにリンクする可能性があります。

これはエラーでしょうか?

従来、C ++を使用すると、有効な使用法ではないように見えるかもしれないが、言語の構文に適合する操作をユーザーが実行できます。

確かに、これはエラーとして再分類される可能性がありますが、なぜですか?警告が役に立たないのはどんな目的ですか?

この機能が実際のコードで使用される理論的な可能性がある限り、呼び出された非関数オブジェクトmainが言語に応じてエラーになる可能性はほとんどありません。


という名前の外部から見えるシンボルを作成しますmain。という名前の外部から見える関数を持たなければならない有効なプログラムは、どのようにmainしてそれにリンクすることができますか?
Keith Thompson、

@KeithThompson実行時にロードします。明確になります。
Michael Gazonda、2015年

シンボルタイプの違いを区別できないためです。リンクはうまく機能します-実行(注意深く作成された場合を除く)は機能しません。
Chris Stratton

1
@ChrisStratton:キースの主張は、シンボルが複数定義されているためにリンクが失敗するということだと思います... main関数を定義しない限り「有効なプログラム」は有効なプログラムではないためです。
Ben Voigt 2015年

@BenVoigtしかし、ライブラリに表示された場合、リンクは失敗しません(おそらく失敗しません)。これは、プログラムのリンク時にint main;定義が表示されないためです。

6

実際の言語基準を引用することですでに与えられた答えに加えたいと思います。

「int main」です。有効なCプログラム?

短い答え(私の意見):あなたの実装が「自立型実行環境」を使用している場合のみ。

C11からの以下の引用すべて

5.環境

実装は、Cソースファイルを翻訳し、2つのデータ処理システム環境でCプログラムを実行します。これらは、翻訳環境と実行環境と呼ばれます[...]

5.1.2実行環境

独立型とホスト型の2つの実行環境が定義されています。どちらの場合も、指定されたC関数が実行環境によって呼び出されると、プログラムの起動が発生します。

5.1.2.1独立型環境

独立した環境(オペレーティングシステムの利点なしにCプログラムの実行が行われる可能性がある)では、プログラムの起動時に呼び出される関数の名前とタイプは実装定義です。

5.1.2.2ホスト環境

ホスト環境を提供する必要はありませんが、存在する場合は次の仕様に準拠する必要があります。

5.1.2.2.1プログラムの起動

プログラムの起動時に呼び出される関数の名前はmainです。[...]戻り値の型がintでパラメータなし[...]または2つのパラメータ[...]または同等のもの、または他の実装定義の方法で定義されます。

これらから、以下が観察されます。

  • C11プログラムは、独立型またはホスト型の実行環境を持つことができ、有効にすることができます。
  • 自立型の場合は、メイン関数が存在する必要はありません。
  • それ以外の場合は、int型の戻り値を持つものが必要です。

自立型実行環境では、5.1.2で必要な機能がないため、起動を許可しない有効なプログラムであると私は主張します。ホストされた実行環境では、コードがmainという名前のオブジェクトを導入する一方で、戻り値を提供できないため、この意味で有効なプログラムではないと主張します。 (たとえば、データのみを提供したい場合などに)実行されることを意図しており、それだけでは実行できません。

「int main」です。有効なC ++プログラム?

短い答え(私の意見):あなたの実装が「自立型実行環境」を使用している場合のみ。

C ++ 14からの引用

3.6.1主な機能

プログラムには、プログラムの指定された開始点であるmainと呼ばれるグローバル関数が含まれます。メイン関数を定義するために自立環境のプログラムが必要かどうかは、実装定義です。[...]戻り値の型はint型ですが、それ以外の場合はその型は実装定義です。[...] mainという名前は他の方法で予約されていません。

ここでは、C11標準とは異なり、起動機能がまったく言及されていないため、自立型実行環境に適用される制限は少なくなりますが、ホスト型実行環境の場合は、C11の場合とほとんど同じです。

繰り返しになりますが、ホスト型の場合、あなたのコードは有効なC ++ 14プログラムではないと私は主張しますが、それは自立型の場合のものであると確信しています。

私の回答は実行環境のみを考慮しているため、翻訳環境で発生する名前のマングリングが事前に発生するため、dasblinkenlichtによる回答が関係していると思います。ここでは、上記の引用が厳密に守られているかどうかはわかりません。


4

私のポイントは、おそらく、これはホストされた環境ではエラーになるはずだと私は本当に思っているということです。

エラーはあなた次第です。mainを返すという名前の関数を指定せずint、ホスト環境でプログラムを使用しようとしました。

という名前のグローバル変数を定義するコンパイル単位があるとしますmain。プログラムを構成するものは自立環境での実装に任されているため、これは自立環境では合法である可能性があります。

mainを返し、int引数を取らないという名前のグローバル関数を定義する別のコンパイル単位があるとします。これはまさに、ホストされた環境のプログラムに必要なものです。

独立した環境で最初のコンパイルユニットのみを使用し、ホストされた環境で2番目のコンパイルユニットのみを使用する場合は、すべて問題ありません。1つのプログラムで両方を使用するとどうなりますか?C ++では、1つの定義ルールに違反しています。それは未定義の動作です。Cでは、単一のシンボルへのすべての参照が一貫していなければならないことを指示する規則に違反しています。そうでない場合は、未定義の動作です。未定義の動作は「刑務所から抜け出す、無料です!」実装の開発者へのカード。未定義の動作に応じて実装が行うことはすべて、標準に準拠しています。実装は、未定義の動作を検出するだけでなく、警告する必要もありません。

これらのコンパイルユニットの1つだけを使用していて、間違ったものを使用している場合(これはあなたがしたことです)はどうなりますか?Cでは、状況は明確です。mainホスト環境で2つの標準形式のいずれかで関数を定義しなかった場合の動作は未定義です。まったく定義mainしなかったとします。コンパイラ/リンカーは、このエラーについて何かを言う必要はありません。彼らが文句を言うことは彼らのために素晴らしいことです。Cプログラムがエラーなしでコンパイルおよびリンクされたことは、コンパイラのせいではなく、あなたの責任です。

mainホストされた環境での関数の定義の失敗は、未定義の動作ではなくエラーであるため(つまり、診断する必要があるため)、C ++では少しわかりにくくなります。ただし、C ++の1つの定義ルールは、リンカーがかなり馬鹿げていることを意味します。リンカの仕事は外部参照を解決することであり、1つの定義ルールのおかげで、リンカはそれらの記号の意味を知る必要がありません。という名前のシンボルを指定mainしました。リンカはという名前のシンボルを予期しているmainため、リンカに関する限り、すべて良好です。


4

これまでのCでは、これは実装で定義された動作です。

ISO / IEC9899が言うように:

5.1.2.2.1プログラムの起動

1プログラムの起動時に呼び出される関数の名前はmainです。実装は、この関数のプロトタイプを宣言していません。これは、戻り値の型がintで定義され、パラメーターはありません。

int main(void) { /* ... */ }

または2つのパラメーター(ここではargcおよびargvと呼ばれますが、宣言された関数に対してローカルであるため、どのような名前でも使用できます):

int main(int argc, char *argv[]) { /* ... */ }

または同等のもの; または、他の実装定義の方法で。


3

いいえ、これは有効なプログラムではありません。

C ++の場合、これは最近、欠陥レポート1886:main()の言語リンケージによって明示的に不正な形式にされました:

main()に明示的な言語リンケージを与えることに制限はないようですが、おそらく形式が正しくないか、条件付きでサポートされている必要があります。

解決策の一部には、次の変更が含まれています。

グローバルスコープで変数mainを宣言するプログラム、または(任意の名前空間で)C言語リンケージを使用して名前mainを宣言するプログラムは、形式が正しくありません。

この表現は、C ++ 1zドラフトである最新のC ++ドラフト標準N4527にあります。

clangとgccの両方の最新バージョンでは、これがエラーになります(実際に確認してください)。

error: main cannot be declared as global variable
int main;
^

この不具合報告の前は、診断を必要としない未定義の動作でした。一方、不正な形式のコードは診断を必要とし、コンパイラはこれを警告またはエラーのいずれかにすることができます。


更新していただきありがとうございます!これがコンパイラの診断で取り上げられるのを見るのは素晴らしいことです。ただし、C ++標準の混乱に変化が見られると言わざるを得ません。(背景については、の名前のマングリングに関する上記のコメントを参照してくださいmain()。)main()明示的なリンケージ仕様を持つことを許可しない理由main()は理解していますが、C ++リンケージがあることを強制することは理解していません。もちろん標準は、(ItaniumのABIと言う、)直接ABI連携/名前の符号化を処理する方法アドレスが、実際には、このマングルなりませんmain()_Z4mainv。何が欠けていますか?
Geoff Nixon

supercatのコメントはそれをカバーしていると思います。ユーザー定義のメインを呼び出す前に実装が独自の処理を行っている場合は、代わりにマングル名を呼び出すことを簡単に選択できます。
Shafik Yaghmour 2015年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.