• ベストアンサー

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

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

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

  • ベストアンサー
回答No.4

関数への引数の渡し方には大きく分けて以下の2通りあります。 1.値渡し 呼び出す関数へ「値」を渡して、呼び出された関数でのみその値を 使用します。putcharとかがそれで、putcharに値を渡してあげれば、 あとは、putcharの仕事になるので、putcharの返却値を使用するこ とがありません。 2.参照渡し これがポインタを渡すやり方で、scanfとかがこの方法になります。 scanf("%d", &a); などとaの前に&をつけますよね。この&をつけることにより、aのアド レスを渡してあげるのです。それにより、呼び出し元の関数でaの 値を使用することができます。 で、文字列を扱う場合なんですが、C言語には文字列をそのまま関 数に渡す方法がないのです。そこで仕方なく文字列の先頭のアドレ スを渡してあげるのです。そうすることにより、呼び出された関数の 中で、文字列を扱うことができます。先頭のアドレスさえわかれば、 あとはそれにつづくアドレスの値が文字列になるのですから・・・。 で、「*a」とか「a[]」とかの書き方で受け取る側は定義します。値とし ては「*a」も「a[]」も同値です。 ただ、「*a」と定義した場合は、aは(文字列の先頭の)ポインタとして 扱いますが、「a[]」と定義した場合は文字列を配列として扱えます。 a[0]なら先頭1文字、a[1]なら先頭から2番目の文字とゆうように。 例.) 文字列が"hello"だった場合 a[]と定義したら、a[0]は'h'がa[1]は'e'が入っている。 *aと定義したら*aは'h'が、a++とした後に*aの値は'e'になる。 その文字列が複数個あるので、「**a」だったり、「*a[]」とゆう書き方 になるのです。 例 a[0]がパラメータ1の文字列(の先頭) a[1]がパラメータ2の  〃 ・・・ とゆう感じです。 正直ここらへんはCの難しいところです。でもここを理解できれば、あ とはほとんど覚えるだけの作業になるので頑張ってください(^_^)

nbsp0606
質問者

お礼

頂いた回答をすんなり理解できるレベルになかったため、 ポインタについて、さらに詳しく参考書で勉強しておりました。 お礼が遅れて、すみません。 >C言語には文字列をそのまま関数に渡す方法がないのです。 >そこで仕方なく文字列の先頭のアドレスを渡してあげる 大変参考になりました。 頂いた回答は全体的に、大事なことが沢山詰まっており、 とても分かりやすかったです。 ありがとうございました。

その他の回答 (8)

回答No.9

例えば、下の様に動かして見るとどうなるか考えて見てください。 command arg1 arg2 filename argv[0] = "command" argv[1] = "arg1" argv[2] = "arg2" argv[3] = "filename" 上記をコピーしようと思うと、 char **copyarg; copyarg = (char **)malloc( sizeof(char *) * 4 ); /*文字列の配列*/ copyarg[0]=(char *)malloc( sizeof(char) * ( strlen(argv[0]) + 1) ); strcpy( copyarg[0], argv[0] ); /* "command" 文字列 */ copyarg[1]=(char *)malloc( sizeof(char) * ( strlen(argv[1]) + 1) ); strcpy( copyarg[1], argv[1] ); /* "arg1" 文字列 */ copyarg[2]=(char *)malloc( sizeof(char) * ( strlen(argv[2]) + 1) ); strcpy( copyarg[2], argv[2] ); /* "arg2" 文字列 */ copyarg[3]=(char *)malloc( sizeof(char) * ( strlen(argv[3]) + 1) ); strcpy( copyarg[3], argv[3] ); /* "filename" 文字列 */ char argv[4][9] ではありません。(全文字列用に最大の9文字分のメモリが割り当てられている訳ではありません) argv[0][8] argv[1][5] argv[2][5] argv[3][9] char *argv[4] もしくは char **argvです。 下記のソースをコンパイルして動かしてみてください。 引数に与える文字列の長さを色々変えて確認してみてください。 アドレスの差を見ると、文字列長+1 になるのではないかな。(最大文字の配列なら全部同じ差になるはず) メモリが無駄に使われてない事が解るのではないかな。 ※ argv[m][n]の様に全部の文字列を最大文字列長で確保している訳ではない。 #include <stdio.h> int main( int argc, char *argv[] ) { int n; for( n = 0; argc >n; n ++ ) { printf( "No.%d Addr:0x%x String:%s\n", n, argv[n], argv[n] ); } } No.0 Addr:0xbfbfecd8 String:./a.out No.1 Addr:0xbfbfece0 String:command No.2 Addr:0xbfbfece8 String:arg1 No.3 Addr:0xbfbfeced String:arg2 No.4 Addr:0xbfbfecf2 String:filename No.0 Addr:0xbfbfecd4 String:./a.out No.1 Addr:0xbfbfecdc String:a No.2 Addr:0xbfbfecde String:BC No.3 Addr:0xbfbfece1 String:def No.4 Addr:0xbfbfece5 String:GHIJK No.5 Addr:0xbfbfeceb String:lmnopurstuv No.6 Addr:0xbfbfecf7 String:WXY a 2 ( 0xbfbfecde - 0xbfbfecdc ) BC 3 ( 0xbfbfece1 - 0xbfbfecde ) def 4 ( 0xbfbfece5 - 0xbfbfece1 ) ※ "def" は 'd' 'e' 'f' '\0' ですから 4 バイトですね。

nbsp0606
質問者

お礼

>メモリが無駄に使われてない事が解るのではないか なるほど、勉強になりました。 参考にさせて頂きます。 ありがとうございました。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.8

#2 の「引数の扱いを平易にするため」というのは, たとえばこんなことです: プログラムを foo arg1 arg2 longlongarguments と起動した場合, 今の C の仕様 (char *[] で渡す) だと argv[0]: "foo" argv[1]: "arg1" argv[2]: "arg2" argv[3]: "longlongarguments" argv[4]: NULL となります. 一方あなたの言うように char [] で渡すと "foo arg1 arg2 longlongarguments" という「文字列」が渡ります. どちらが「より簡単に引数を扱える」と思いますか?

nbsp0606
質問者

お礼

文字列を、バラバラな状態で渡すために、 文字列それぞれの先頭アドレスを配列に入れて渡しているわけですね。 文字の配列と文字列の配列の違いに気付かされました。 ありがとうございます。

  • asuncion
  • ベストアンサー率33% (2127/6289)
回答No.7

>引数2番目がポインタになっている ポインタではなく、「ポインタの配列」です。 Tacosanさんの回答のとおり。

nbsp0606
質問者

お礼

>ポインタではなく、「ポインタの配列」 そうでしたね。 質問当初は、よく分かっておりませんでした。 今はおかげさまで、だいぶ分かるようになりました。 ありがとうございます。

noname#137556
noname#137556
回答No.6

↓の図がわかりやすいかなぁ。

参考URL:
http://ysserve.int-univ.com/Lecture/c2/e_04-03.html
nbsp0606
質問者

お礼

ポインタの説明には、確かにメモリの図があった方が分かりやすいですね。 私は参考書の図を見て、理解することができました。 参考になるページを教えて下さり、ありがとうございます。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.5

引数 1個ずつが「文字列」 = char * で, それがたくさんあるからその配列となって char *[], ってだけなんだけどなぁ.... ちなみに関数の仮引数などでは char ** と char *[] は同じ (どちらも char ** と解釈される) ですが, 配列とポインタは本来「違うもの」です.

nbsp0606
質問者

お礼

>引数 1個ずつが「文字列」 = char * で, それがたくさんあるからその配列となって char *[], ってだけなんだけどなぁ.... この説明は、「ポインタの配列」を理解できた後に、理解することができました。 >配列とポインタは本来「違うもの」です. この点も勉強になりました。 ありがとうございます。

回答No.3

まず、文字列は char a[10]; とかで表現しますよね? そうするとパラメータで渡す場合は「*a」または「a[]」となります。 では、複数の文字列は char a[5][10] とかで表現しますよね? そうするとパラメータで渡す場合は「**a」または「a[][]」または「*a[]」と なります。 引数では a.exe パラメータ1 パラメータ2 と複数のパラメータを指定したい場合があります。 そうすると複数文字列の場合になるので、「argv[]」ではダメで 「*argv[]」となります。 ちなみに「**arg」と書いても問題ないし、そう書く人もいます。

nbsp0606
質問者

お礼

>そうするとパラメータで渡す場合は「*a」または「a[]」 すみません、この段階で既につまずいております。 ただ、これが分かれば、後はスムーズに理解できそうだと思っているのですが…。 ポインタの使用意義が私はまだよく分かっていないようです。 >「**a」または「a[][]」または「*a[]」 そもそも、これらは全て、同値なのでしょうか。 もう、この辺りはさっぱりわかりません。すみません。泣

  • D-Matsu
  • ベストアンサー率45% (1080/2394)
回答No.2

argvの型はchar **と書かれることもあります。 まぁそんなことはさておきますが、引数が複数あった場合に(char *)[]なら単純にargvを配列参照すれば各引数にアクセスできますが、char []だとわざわざ分割して解析して、という手順が必要になります。 ということで「引数の扱いを平易にするため」ということでどうですか?

nbsp0606
質問者

お礼

>(char *)[]なら単純にargvを配列参照すれば各引数にアクセスできますが、char []だとわざわざ分割して解析して、という手順が必要に (char *)[] こういった書き方もあるのでしょうか? >配列参照すれば各引数にアクセスできます >わざわざ分割して解析 この辺りが、おそらく本件の回答の核であるように感じましたが、 初心者の私には、上記の意味するところがよく分かりませんでした。 もし可能でしたら、それが意味する内容を、もう少し噛み砕いてご説明頂けますと嬉しいです。 回答、ありがとうございます。

  • titokani
  • ベストアンサー率19% (341/1726)
回答No.1

複数の引数を渡しやすいようにするためでしょう。

nbsp0606
質問者

お礼

すみません、まだまだ初心者であるため、 おっしゃることの意味がよく分かりませんでした。 ただ、複数の引数の渡しやすさに、しやすさ・しにくさがあって、 ポインタ形式だと(なぜかは分かりませんが)、しやすくなるらしい、 ということが分かりました。 ありがとうございます。

関連するQ&A