web-dev-qa-db-ja.com

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

私のコンパイラーはそうではないと思っているようです。

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

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

それ以外の場合は、コンパイルとリンクは問題ありません。しかし、実行時には、SIGBUS(私にとって)ですぐに終了します。

(main()はCとC++で何を返す必要がありますか? および言語仕様の簡単なgrepで(優れた)回答を読むと、確かにseemメイン関数が必要であることを私に。しかし、gccの-Wmain( 'main' is通常関数)(およびエラーの不足)別の方法で提案する。

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

私のポイントは、これがホスト環境ではerrorであると本当に思うべきだということです。

111
Geoff Nixon

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

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

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

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

ファイルx.c:

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

ファイルy.c:

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

コンパイル:

gcc x.c y.c

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

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

97
dasblinkenlight

main予約語ではありませんそれは定義済み識別子cinのように、 endlnpos...)。したがって、mainという変数を宣言し、初期化してからその値を出力できます。

もちろん:

  • これは非常にエラーが発生しやすいため、警告は役立ちます。
  • main()関数(ライブラリ)なしでソースファイルを作成できます。

編集

いくつかの参照:

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

    関数mainはプログラム内では使用されません。 mainのリンケージ(3.5)は実装定義です。 mainを削除済みとして定義するプログラム、または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の場合です。その識別子を使用する宣言がその意味を再定義するスコープがあります。

30
manlio

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

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

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

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

ホスト環境ではnot有効です。

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

同上。

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

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

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

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

gccは独立した環境ですか、それともホストされた環境ですか?

はい。

gcc-ffreestandingコンパイルフラグ。追加すると、警告は消えます。あなたが構築するときにそれを使用したい場合がありますカーネルまたはファームウェア。

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

19
n.m.

技術的に禁止されていないため、警告です。スタートアップコードは、「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 .

17
dascandy

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

いいえ

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

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

はい

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

はい

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

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

これはエラーになりますか?

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

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

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

9
Michael Gazonda

実際の言語標準を引用して、すでに与えられた答えに追加したいと思います。

「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プログラムではないと主張しますが、自立型の場合に適していると確信しています。

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

6

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

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

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

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

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

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

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

4
David Hammen

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

ISO/IEC9899が言うように:

5.1.2.2.1プログラムの起動

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

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

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

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

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

4
dhein

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

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

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

また、解像度の一部には次の変更が含まれています。

グローバルスコープで変数mainを宣言するプログラム、またはC言語リンケージで名前mainを宣言する(任意のネームスペースで)プログラムは不正です。

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

Clangとgccの両方の最新バージョンは、これをエラーにします(ライブを見る):

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

この障害レポートの前は、診断を必要としない未定義の動作でした。一方、不正な形式のコードには診断が必要であり、コンパイラはこれを警告またはエラーにすることができます。

3
Shafik Yaghmour