次のCプログラムを検討してください(ライブデモを参照 ここ )。
const int main = 195;
現実の世界では、このようなコードを書くプログラマーはいないことを私は知っています。なぜなら、それは有用な目的を果たさず、意味をなさないからです。しかし、プログラムの上からconst
キーワードを削除すると、すぐに セグメンテーション違反 になります。どうして?私はこの背後にある理由を知りたいと思っています。
GCC 4.8.2は、コンパイル時に次の警告を出します。
警告:「main」は通常関数です[-Wmain]
const int main = 195; ^
const
キーワードの有無が、プログラムの動作に違いをもたらすのはなぜですか?
値195が8086互換のret
(関数からの戻り)命令にどのように対応するかを観察します。したがって、このmain
の定義は、実行時にint main() {}
として定義したかのように動作します。
一部のプラットフォームでは、const
データは実行可能であるが書き込み可能ではないメモリ領域にロードされますが、可変データ(つまり、修飾されていないconst
)は書き込み可能であるが実行可能ではないメモリ領域にロードされます。このため、プログラムはmain
をconst
として宣言すると「機能」しますが、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つのパラメーター(...)または同等のもので定義する必要があります。または他の実装定義の方法で。
Cでは、グローバルスコープのmain
はほとんどの場合関数です。
グローバルスコープで変数としてmain
を使用すると、プログラムの動作が未定義になります。
(それはかもしれないconst
を書くとき、コンパイラが変数を定数に最適化するので、プログラムの振る舞いが異なる場合です。しかし、プログラムの振る舞いはまだ未定義)。