[解決済み] C言語のmain()メソッドはどのように動作するのですか?
質問
mainメソッドの書き方として、2種類のシグネチャがあることは知っています。
int main()
{
//Code
}
また、コマンドライン引数を扱う場合は、次のように記述します。
int main(int argc, char * argv[])
{
//code
}
で
C++
メソッドをオーバーロードできることは知っていますが、その中で
C
の2つの異なるシグネチャをコンパイラはどのように処理するのでしょうか?
main
関数を使用できますか?
どのように解決するのですか?
C言語の機能の中には、たまたまうまくいったハッキングから始まったものもある。
mainの複数署名や可変長引数リストもその一つです。
プログラマーは、関数に余分な引数を渡しても、与えられたコンパイラで何も悪いことが起きないことに気づきました。
というような呼び出し規約がある場合です。
- 呼び出し側の関数が引数をクリーンアップする。
- 左端の引数はスタックの先頭、またはスタックフレームの基部に近いので、偽の引数がアドレッシングを無効にすることはありません。
これらのルールに従った呼び出しの慣習の1つは、スタックベースのパラメータ渡しで、呼び出し側が引数をポップして、右から左へプッシュします。
;; pseudo-assembly-language
;; main(argc, argv, envp); call
push envp ;; rightmost argument
push argv ;;
push argc ;; leftmost argument ends up on top of stack
call main
pop ;; caller cleans up
pop
pop
このような呼び出し規約があるコンパイラでは、2種類の
main
また、追加された種類もあります。
main
は引数なしの関数で、この場合、スタックにプッシュされたアイテムは無視されます。もしそれが2つの引数を持つ関数なら、それは
argc
と
argv
をスタックの一番上の2つの項目とします。もしそれがプラットフォーム固有の 3 つの引数と環境ポインタ (一般的な拡張子) のバリエーションであれば、それも動作します: スタックの先頭から 3 番目の要素としてその 3 つの引数を見つけます。
こうして、固定コールはあらゆるケースで機能し、単一の固定された起動モジュールをプログラムにリンクさせることができます。このモジュールはC言語で、次のような関数として書くことができる。
/* I'm adding envp to show that even a popular platform-specific variant
can be handled. */
extern int main(int argc, char **argv, char **envp);
void __start(void)
{
/* This is the real startup function for the executable.
It performs a bunch of library initialization. */
/* ... */
/* And then: */
exit(main(argc_from_somewhere, argv_from_somewhere, envp_from_somewhere));
}
つまり、このスタートモジュールは、常に3つの引数を持つmainを呼び出すだけなのです。もしmainが引数を取らないか、あるいは
int, char **
は、呼び出しの規約により、引数を取らない場合と同様に、たまたま正常に動作しているのです。
もし、このようなことをプログラムで行うとしたら、それは非移植的であり、ISO Cでは未定義の動作とみなされます。ある方法で関数を宣言して呼び出し、別の方法でそれを定義することです。しかし、コンパイラのスタートアップ・トリックは移植可能である必要はなく、移植可能なプログラムのための規則によって導かれるものではありません。
しかし、呼び出しの規約がこのように動作できないようなものだったとします。その場合、コンパイラは
main
は特別なものです。コンパイル中に
main
関数を呼び出すと、例えば3つの引数の呼び出しに対応したコードを生成することができます。
つまり、こう書くのです。
int main(void)
{
/* ... */
}
しかし、コンパイラはこれを見ると、本質的にコード変換を行い、コンパイルする関数がより次のように見えるようにします。
int main(int __argc_ignore, char **__argv_ignore, char **__envp_ignore)
{
/* ... */
}
ただし、名前
__argc_ignore
は文字通り存在しないのです。そのような名前がスコープに入ることはありませんし、未使用の引数に関する警告も出ません。
このコード変換により、コンパイラは3つの引数をクリーンアップしなければならないことを知っている正しいリンケージを持つコードを出力するようになります。
もうひとつの実装方法として、コンパイラやリンカがカスタムで
__start
関数 (またはその呼び名) を使用するか、少なくとも事前にコンパイルされたいくつかの選択肢の中から1つを選択します。のどの形式がサポートされているかという情報をオブジェクトファイルに保存することができます。
main
が使用されています。 リンカはこの情報を見て、スタートアップモジュールの正しいバージョンを選択することができます。
main
そのプログラムの定義と互換性のあるものです。 C言語の実装では、通常、サポートされている
main
ということで、この方法は実現可能です。
C99言語用のコンパイラは、常に
main
を付けずに関数を終了させるというハックをサポートするために、ある程度、特別なものにしています。
return
ステートメントを使用すると、あたかも
return 0
が実行されました。 これも、コード変換で処理することができる。コンパイラは
main
がコンパイルされている。そして、ボディの末尾に到達できる可能性があるかどうかをチェックする。もしそうなら
return 0;
関連
-
[解決済み】デバッグアサーションに失敗しました。C++のベクトル添え字が範囲外
-
[解決済み] explicit キーワードの意味は?
-
[解決済み] 文字列の単語を反復処理するにはどうすればよいですか?
-
[解決済み] SQLiteのINSERT/per-secondのパフォーマンスを向上させる
-
[解決済み] 1ビットのセット、クリア、トグルはどのように行うのですか?
-
[解決済み] C言語では「?」演算子は何をするのですか?
-
[解決済み] C++11では、標準化されたメモリモデルが導入されました。その意味するところは?そして、C++プログラミングにどのような影響を与えるのでしょうか?
-
[解決済み] CとC++でmain()は何を返すべきですか?
-
[解決済み] int argc, char *argv[] とはどういう意味ですか?
-
[解決済み】C/C++の"-->"演算子とは何ですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] テスト
-
[解決済み】C++ クラスヘッダが含まれているときに「不明な型」があるのはなぜですか?重複
-
[解決済み】C++でint型に無限大を設定する
-
[解決済み】LLVMで暗黙のうちに削除されたコピーコンストラクタの呼び出し
-
[解決済み】C++ - 解放されるポインタが割り当てられていないエラー
-
[解決済み】「corrupted size vs. prev_size」glibc エラーを理解する。
-
[解決済み】浮動小数点例外エラーが発生する: 8
-
[解決済み】演算子のオーバーロード C++; <<操作のパラメータが多すぎる
-
[解決済み] 警告:暗黙の定数変換でのオーバーフロー
-
[解決済み] 変数サイズのオブジェクトが初期化されないことがある c++