[解決済み] C言語のmain()メソッドはどのように動作するのでしょうか?
質問
mainメソッドの書き方として、2種類のシグネチャがあることは知っています。
int main()
{
//Code
}
また、コマンドライン引数を扱う場合は、次のように記述します。
int main(int argc, char * argv[])
{
//code
}
で
C++
メソッドをオーバーロードすることができるのは知っていますが
C
の二つの異なるシグネチャをコンパイラはどのように扱うのでしょうか?
main
関数をどのように扱うのでしょうか?
どのように解決するのですか?
C言語の機能のいくつかは、たまたま動作するようになったハックから始まりました。
可変長の引数リストと同様に、mainのための複数の署名は、それらの機能の1つです。
プログラマは、関数に余分な引数を渡しても、与えられたコンパイラで何も悪いことが起きないことに気づきました。
呼び出しの規約がそのようなものである場合、このようになります。
- 呼び出し側の関数が引数をクリーンアップする。
- 一番左の引数はスタックの一番上、またはスタックフレームの底に近いので、 偽の引数がアドレッシングを無効にすることはありません。
これらのルールに従った呼び出しの慣習の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
の2種類、あるいは追加の種類をサポートするために特別なことをする必要はありません。
main
は引数なしの関数にすることができ、その場合スタックにプッシュされた アイテムは無視されます。もしそれが2つの引数を持つ関数であれば、それは
argc
と
argv
をスタックの一番上の 2 つの項目として返します。もしそれが環境ポインタ (一般的な拡張) を持つプラットフォーム固有の 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;
関連
-
[解決済み】std::cin.getline( ) vs. std::cin
-
[解決済み] 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++でユーザー入力を待つ【重複あり
-
[解決済み】致命的なエラー LNK1169: ゲームプログラミングで1つ以上の多重定義されたシンボルが発見された
-
[解決済み] 既に.objで定義されている-二重包含はない
-
[解決済み】C++プログラムでのコンソールの一時停止
-
[解決済み】c++でstd::vectorを返すための効率的な方法
-
[解決済み】クラステンプレートの使用にはテンプレート引数リストが必要です
-
[解決済み] gdbを使用してもデバッグシンボルが見つからない
-
[解決済み】Eclipse IDEでC++エラー「nullptrはこのスコープで宣言されていません」が発生する件
-
[解決済み] 変数サイズのオブジェクトが初期化されないことがある c++