web-dev-qa-db-ja.com

const int main = 195でプログラムが機能するのに、constがないとセグメンテーション違反で終了するのはなぜですか?

次のCプログラムを検討してください(ライブデモを参照 ここ )。

const int main = 195;

現実の世界では、このようなコードを書くプログラマーはいないことを私は知っています。なぜなら、それは有用な目的を果たさず、意味をなさないからです。しかし、プログラムの上からconstキーワードを削除すると、すぐに セグメンテーション違反 になります。どうして?私はこの背後にある理由を知りたいと思っています。

GCC 4.8.2は、コンパイル時に次の警告を出します。

警告:「main」は通常関数です[-Wmain]

const int main = 195;
          ^

constキーワードの有無が、プログラムの動作に違いをもたらすのはなぜですか?

32
Destructor

値195が8086互換のret(関数からの戻り)命令にどのように対応するかを観察します。したがって、このmainの定義は、実行時にint main() {}として定義したかのように動作します。

一部のプラットフォームでは、constデータは実行可能であるが書き込み可能ではないメモリ領域にロードされますが、可変データ(つまり、修飾されていないconst)は書き込み可能であるが実行可能ではないメモリ領域にロードされます。このため、プログラムはmainconstとして宣言すると「機能」しますが、const修飾子を省略した場合は機能しません。

従来、バイナリには3つのセグメント:が含まれていました

  • textセグメントは(アーキテクチャでサポートされている場合)書き込み保護されて実行可能であり、実行可能コード、static保存期間修飾constの変数、および文字列リテラルが含まれています
  • dataセグメントは書き込み可能であり、実行できません。これには、constで修飾されていない変数が含まれています静的ストレージ期間および(実行時)オブジェクトで割り当て済みストレージ期間
  • bssセグメントはdataセグメントに似ていますが、すべてゼロに初期化されます。初期化子なしで宣言された静的修飾されていないストレージ期間constの変数が含まれています
  • stackセグメントはバイナリに存在せず、自動ストレージ期間の変数が含まれています

変数constからmain修飾子を削除すると、textから実行不可能なdataセグメントに移動され、セグメンテーションが発生します。あなたが観察する違反。

最近のプラットフォームには、さらにセグメントがあることがよくあります(たとえば、書き込み可能でも実行可能でもないデータのrodataセグメント)。したがって、プラットフォーム固有のドキュメントを参照せずに、これをプラットフォームの正確な説明と見なさないでください。

技術的にはプラットフォームでmainを変数として宣言できる場合もありますが、mainを関数にしないことは通常正しくないことを理解してください。 ISO 9899:2011§5.1.2.2.1¶1、強調鉱山:

1プログラムの起動時に呼び出される関数の名前はmainです。実装は、この関数のプロトタイプを宣言していません。戻り値の型intで、パラメーターなし(...)または2つのパラメーター(...)または同等のもので定義する必要があります。または他の実装定義の方法で。

59
fuz

Cでは、グローバルスコープのmainはほとんどの場合関数です。

グローバルスコープで変数としてmainを使用すると、プログラムの動作が未定義になります。

(それはかもしれないconstを書くとき、コンパイラが変数を定数に最適化するので、プログラムの振る舞いが異なる場合です。しかし、プログラムの振る舞いはまだ未定義)。

11
Bathsheba