• ベストアンサー

C言語でのコマンドライン引数の内部での処理のされ方

C言語でint main(int argc, char *argv[])とメイン関数を宣言します。 2番目の引数はC言語の文法的にいうと文字列へのポインタの配列だとおもいますが、一般的な関数でこの引数に値を渡すとすると、以下のように宣言されたポインタ配列を渡すことになるとおもいます。 ・宣言 char *pa[]; ・関数への渡し func(pa); 話が元に戻りますが、main関数でもらう場合は、プログラム外部から与えられた引数は(正確に言うとアドレス)、メモリ上ではC言語で書かれたexeファイルの外から実行時にプログラムファイルのメモリ上にコピーされるのでしょうか? 自分でもうまく表現できないのですが、 ・コマンドプロンプトで引数を与えて実行         ↓ ・プログラムファイルのメモリ上に引数がロードされる ということでいいんでしょうか? 自分でもなんだかうまく表現できないので、お暇な方でよろしいので、気が向いた人、回答ください。 よろしくお願いします。

質問者が選んだベストアンサー

  • ベストアンサー
  • Oh-Orange
  • ベストアンサー率63% (854/1345)
回答No.3

★ちょっと昔話。 ・Windows より前の OS MS-DOS では、プログラムが実行されるとメモリ上に存在する  EXE ヘッダにコマンドラインの文字列と文字数が格納されます。  そして、C 言語で作成されたプログラムは main() 関数を実行する前にスタートアップ  ルーチンという処理が行われます。この処理の中に EXE ヘッダに存在するコマンドライン  文字列のコピーを作ります。その後、スペースで区切られた引数を char *argv[] に  セットしてから main() 関数が呼び出されます。 ・よって、まとめると  (1)コマンドラインから実行  (2)プログラムをメモリ上にロード(EXE ヘッダに引数文字列がセットされる)  (3)スタートアップ処理で EXE ヘッダから C 言語のグローバル変数領域に引数文字列をコピー  (4)コピーされた引数文字列をスペース文字で区切って char *argv[] 配列にセット  (5)main() 関数に char *argv[] と区切った引数の個数を argc で渡す  ↑  簡単に説明するとこんな感じになります。 ・引数文字列はコピーしたものを *argv[] で使うため EXE ヘッダにある引数文字列は  書き換わりません。→正しくは EXE ヘッダにある引数文字列は書き換えてはいけない。 ・上記のは MS-DOS という OS の話です。でも Windows のプログラムも同様な処理を行っている  と思います。EXE ヘッダ(PE情報)があるので。MS-DOS 時代よりも PE ヘッダが複雑でサイズが  増えていますね。この PE 情報の資料がほとんど見つかりませんね。英語サイトならありますが…。 最後に: >・プログラムファイルのメモリ上に引数がロードされる  ↑  プログラムファイルのメモリ上(EXEヘッダ)に引数文字列がロードされる  その後、プログラムのグローバル変数領域に引数文字列がコピーされる  そして、コピーされた引数文字列をスペース文字で分割される  そしたら main() 関数の char *argv[] として引数文字列が渡される  という順になります。 ・ちなみに Windows API 関数に引数文字列を取得する GetCommandLine() があります。  これは多分、EXE ヘッダの引数文字列から直接ポインタで取得しているような気がします。  理由は、この関数で取得した引数文字列は書き換えないようにという注意書きがあるので。  でも、本当のところはよく分かりませんが…。注意書きを信じてコピーしてから使いましょう。 ・他の OS については詳しく知りませんので 昔の MS-DOS、そして Windows 系で回答してみました。 ・以上。

参考URL:
http://wisdom.sakura.ne.jp/system/winapi/win32/win6.html
kamkamkam3
質問者

お礼

どうも、大変ご丁寧な解説ありがとうございます。 自分は、一応プログラム暦(アマグラム暦?)6年になります。コンピュータの内部動作とかまでようやくりかいできるようになり、今回、このような質問をした次第です。 その上でOh-Orange様のご回答は非常に参考になりました。WindowsでもUNIXでもいいのですが、とにかく内部的な動作がしりたかったのです。それで、Windows自体のCプログラムの呼び出し具合を書いていただいたので、この上ない、私自身のありがたい知識となりました。 MS-DOS時代からの方でいらっしゃるんですね。私はほとんどWinXPですので、昔の技術も興味あります。UNIXとか必死で勉強しています。 今回は大変ご丁寧な回答誠にありがとうございました。非常に勉強になりました。感謝しきれないくらいです。ありがとうございました。

その他の回答 (4)

  • jacta
  • ベストアンサー率26% (845/3158)
回答No.5

ホスト処理系では、最初にmainが呼び出された後のことしか、規格上は扱われていません。つまり、それ以前のことは処理系が勝手に決めることですので、一般論で語っても意味ありません。 ちなみに、先ほど「最初に」と書いたのは、mainを再帰的に呼び出すことも可能だからです。その場合は、アプリケーション側でどんな風にメモリを割り付けるかによります。

kamkamkam3
質問者

お礼

どうもありがとうございます。 処理系によるんですね。今回は色々なかたのお話をお伺いし、OSがどの辺までCプログラムなどを扱うのかがなんとなく分かった気がします。今までは、OSとアプリケーションの境界線が漠然としていたために、少し疑問におもっていました。 しかし、皆さん、お詳しいですね。どこで、勉強するんですかね・・私自身書店などで結構立ち読みするのですが、全然、その辺の知識が身に付きません・・ 今回は勉強になりました。本当にありがとうございました。

  • rabbit_cat
  • ベストアンサー率40% (829/2062)
回答No.4

#2さんの通り、たいていプログラムの起動時に _CRTStartup() とかいった名前の関数が呼ばれて、そこで、コマンドライン引数などの解析(argc,argvへの格納)をして、main()を呼び出すって感じの処理をすしてます。 例えば、いにしえのDOSの時代には、 アドレス 80h にコマンドライン引数が格納されていました。 http://www5c.biglobe.ne.jp/~ecb/assembler2/1_1.html

kamkamkam3
質問者

お礼

どうもありがとうございました。 #2様のおっしゃるとおりなんですね。私は、諸事情で大学すら行っていないので、深い知識は全然分からないので、ここの皆さんの知識には驚かされるばかりです。参考URLも見させていただきましたが、どこでそういったものは、見つけてくるのですかね・・ なにしろ、困ったらここで聞くしかないので。。 どうもありがとうございました。MS-DOSの頃のような基本的なことから勉強したいです。でも、その環境作りも今じゃ難しいですよね。なんとか、しがみついて、勉強していこうとおもいます。 目標は80歳で名を挙げることです・・

  • isle
  • ベストアンサー率51% (77/150)
回答No.2

C言語の規格では、mainエントリ関数に渡されたコマンドライン文字列は書き換え可能ということになっています。 #元の文字列の長さを超えて書き込むとどうなるかは分かりません。 ということから、mainに来る前にメモリが確保されて文字列がコピーされている、と考えることができるのではないでしょうか。

kamkamkam3
質問者

お礼

どうもありがとうございます。 isle様の教えていただいたことから推測すると、確かに、mainに来る前に文字列がコピーされていると考えることが、確かなようですね。 C言語の知識は、つけようと思えば、いくらでも本に載っていますが、その辺のOSとのやりとりというか、OS内部部分まで関わってくると、なかなか、知識の身につけようがないですね。まして、昨今のパソコンOSは、非常に高度みたいですし。。なにか、MS-DOS時代がうらやましくなりもします。 ご回答ありがとうございました。参考になりました。

回答No.1

実は、厳密に言えば、C/C++のプログラムで最初に実行されるのは、main() ではありません。 引数の処理や、グローバル変数の初期化など、前処理をするプログラムがあります。 このプログラムが動いて、準備がすべて整った後で、main() という関数がこのプログラムから呼び出されます。 コマンドラインの引数をどのようにして渡すかは、コマンドプロンプトやOSの取り決めになります。その取り決めを理解して、main() がわかるようにセットアップするプログラムが、一番最初に動くということになります。 そういう小さな(小さくないかも)プログラムが、各コンパイラの各OS用に準備されています。これは、ユーザーには見えないうちに処理されてしまいます。 いわゆる「組み込み」の世界で、OSなど無いという段階では、main() を呼ぶまでのスタートアップルーチンも、はっきり見えますが。

kamkamkam3
質問者

お礼

どうもありがとうございます。 考えてみれば、実行ファイルというのは、機械語のレベルですもんね。C言語の文法で考えることがおかしいですね。 AsanoNagiさんが教えてくれたことは、理解できました。たかだかプログラム言語を扱ったところで、コンピュータ全体の動きにかかわることを考えようとしたのは、あさはかだったですね。 ん~。実に難しいですね。掘っても掘っても見つからない温泉のように、なかなか、コンピュータの底まではたどり着けませんね。組み込みをやればわかるということなので、いずれやったとき?は覚えておきます。 ご回答ありがとうございました。大変参考になりました。

関連するQ&A

  • C言語での関数の引数の受け渡しについて

    C言語での関数の引数の受け渡しについて教えてもらいたいのです。 char *p=Goo;  というポインタpがmain関数で定義され、このポインタpをある関数 void func(・・・) に渡すことは出来ますか? つまりポインタを実引数として扱うことはできるのかという事ですが・・・ int p=10; とかだったら、 void func(int test) の関数には、main関数で func(p) で仮引数testにわたせると思うんですが・・・ もし出来るようでしたら、関数の渡し方と定義の記述を教えてください。 どうか宜しくお願いします。

  • コマンドライン引数 *argv[]はなぜポインタ?

    C言語初心者です。 コマンドライン引数、 int main(int argc, char *argv[]) というのを最近勉強しましたが、引数2番目がポインタになっている理由について、 どなたか教えて下さい。 そういう仕様なんだから、それに従いましょう、ということでしょうか? int main(int argc, char argv[]) では、ダメなのでしょうか? このポインタでの引数渡しについて、 なんらかの納得のいく考え方をご存知の方がいらしたら、教えて下さい。 宜しくお願い致します。

  • C言語でコマンドラインの引数について。

    コマンドラインで引数をわたす基本的なプログラムは main(int argc,char *argv[]){....] ですよね。 今回実行時に、 >実行ファイル名 123 + 233 といれたら、123+233を計算してくれるプログラムを作ろうとしています。 その場合、main(int argc,int *argv[]){} としたら、argv[1]に123が入って、argv[2]に+が入って、argv[3]に233が、入るというわけではないんでしょうか? とりあえず、確認で #include<stdio.h> main(int argc,int *argv[]) { printf("%d\n%d\n",argv[1],argv[3]); } としてみたんですが、実行結果は、とても長い数字がでてきてきました。 なにがいけないのか教えてください。 お願いします。 あとWindows MEでVC++6を使ってます。

  • C言語のint型の配列が分かりません

    #include<stdio.h> int main(void) { int str[ ]={0,1,2} printf("%s\n", str); return 0; } というプログラムをC言語でつくってみましたが動きません.(012と表示されて欲しかったのですが) int str[ ]={1,2,3}の部分をchar str[ ]={'0','1','2'}とすれば動きます. そこで質問なのですが, printf("~%s~", (配列名));  はchar型の配列にしか適応できないのですか? ※追記 puts関数の定義は int puts (const char *str); であるそうなので char型の仮引数にはchar型のアドレスを渡さなければいけません. ではprintf関数の定義は一体どんなものなのですか?

  • コマンドプロンプトを使用してのコマンドライン引数

    winXPのコマンドプロンプトを利用して 参考書「独習C」を使い、C言語の学習をしています。 その本の7.4の項目~main()の引数~で main(int argc,char *avgv[])を利用すると コマンドラインから引数をとることができるとありますが 私の学習環境ではうまくいきません。 具体的には #include <stdio.h> int main(int argc, char *argv[]) { int i; for(i=1; i<argc; i++) printf("%s ", argv[i]); return 0; } というプログラムを実行しても何も表示されません。 どうすればコマンドライン引数を利用できますか?

  • C言語

    以下のC言語のプログラムを教えてください。 お願いします。 (1)標準入力から文字列(2 文字以上)を入力し,文字数を計上すると共に,入力された文字列の逆順に入れ替える処理を実現してください.なお,以下の要件を満たしたプログラムを作成してください. ・ 入力された文字列は,char 型の配列(要素数50)で受け取ること ・ 文字数を計上するcount 関数(引数:配列のアドレス,戻り値:文字数)を定義 し,main 関数より呼び出すこと ・ 文字列を逆順に入れ替えるreverse 関数(引数:配列のアドレス,戻り値:無し) を定義し,main 関数より呼び出すこと ・ 標準出力の処理は,main 関数で記述すること 【プロトタイプ宣言】 int count(char *str); void reverse(char *str); 【実行結果】 文字列を入力してください(2 文字以上) apple 文字数 = 5 入れ換え前 apple 入れ換え後 elppa (2)char 型の配列(要素数50)を2 つ宣言し,標準入力から2 つの文字列を入力してください.そして,格納した字列を入れ替える関数(swapstr 関数)を作成し,入れ替え前と入れ替え後の配列内の値(文字列)を配列名とともに標準出力するプログラムを作成してください. 【プロトタイプ宣言】 void swapstr(char *str1, char *str2); 【実行結果】 2 つの文字列を入力してください apple strawberry 入れ換え前 配列str1 = apple 配列str2 = strawberry 入れ換え後 配列str1 = strawberry 配列str2 = apple

  • C言語のポインタによる関数の引数の書き方について教えてください。

    C言語を今勉強中の大学生です。 一気に複数の値をmainに返せる、参照による呼び出しによる関数の引数の書き方で困っています。配列を自作関数に引き渡したいのですが、どう書けばいいのでしょうか。 下の3つは、それぞれ(1)mainからの呼び出し、(2)自作関数での引数の引き受け、です。どこをどう変えたらエラーが出なくなるのか、分かる方、お願いいたします! (1)school(&m,h); (2)void school(int *m, float *h){ ※ちなみに変数は int m,float h[10][10]

  • どなたか、教えていただけませんでしょうか(C言語)?

    皆様のお知恵を拝借したく思いまして 投稿させていただきました C言語についての質問なのですが (1)文字配列の引数を一度mainからプロトタイプ関数へ渡し 関数で宣言して表示。 (2)さらにその関数内で、値を変更して mainで表示する 方法を教えていただきたく思いますので、宜しくお願い致します

  • c言語

    (c++ではなくC89準拠) c言語について質問 (1) 関数名()と関数名(void)は違う意味 (2) mainの引数の型は(void)か(int argc, char *argv[]) (3) K&Rは標準c準拠でmain()という表記があります ということは、(1)の関数名とはmain以外の関数名で、 main()とmain(void)は同じなんでしょうか。

  • C言語のメモリ領域確保

    ポインタ変数ををmain関数で宣言し、関数test()にて必要分だけ領域確保してそのアドレスをmain関数のポインタ変数に渡して利用することは可能でしょうか。 (サイズのわからないテキストデータを、十分に大きな配列に入れるのではなく、関数でメモリを動的確保して無駄の無い配列に入れたい等) C言語ではやはり無理で、構造体のリストにするのが一番でしょうか。 初歩的なことで申し訳ありませんがどなたかお願いいたします。

専門家に質問してみよう