• ベストアンサー

コマンドライン引数 *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% (2126/6286)
回答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

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

    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; } というプログラムを実行しても何も表示されません。 どうすればコマンドライン引数を利用できますか?

  • Cocoa をコマンドラインから実行して引数を受け取りたい

    C・Objective-C に関して全くの初心者ですが、よろしくお願いします。 Cocoa で作ったアプリをコマンドラインから実行して、渡した引数を NSObject のサブクラスで受け取りたいのですがどうしたらよいでしょうか。 main.m の main 関数で受け取る方法はわかったのですが、サブクラス MyObject.m 内の任意のメソッドに渡す方法がわかりません。 // 引数を受け取って表示する int main(int argc, char *argv[]) {   fprintf(stdout, "%s\n", argv[1]);   return NSApplicationMain(argc, (const char **) argv); } ご教示いただけますと幸いです。

  • main(int argc,char **argv[])の意味を教えて下さい

     今晩は、Cの初心者です宜しくお願いします。  main関数の引数で、int main(int argc,char **argv[])とint main(int argc,char *argv[])と書かれている場合がありますが、 「**argv」と「*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言語でのコマンドライン引数の内部での処理のされ方

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

  • コマンドライン引数argv[]について

    C言語初心者です。以下のようなプログラムを書いたのですが、コンパイルで来ません。関数func1, func2を分けずに全てメインに書けば動作するのですが、なぜ関数に分けてしまうとコンパイル出来ないのかがわかりません。エラー内容は、argvが宣言されていない、といった内容です。 #include <stdio.h> void func1(void){ printf("%s\n", argv[1]); } void func2(void){ printf("%s\n", argv[2]); } int main(int argc, char *argv[]){ if(strcmp(argv[1], "abc")==0){ func1(); } else{ func2(); } }

  • ポインタについて

    こんにちは。「独習C」で独学している者です。その中の練習問題をやっていました。課題はコマンドラインから引数を受け取り各文字に1を加え暗号化して表示するというものでした。 自分で考え一応ちゃんと動くものが書けました。その後もっとシンプルにならないかと少しずつ余分な所を削っていき以下のようになりました。 #include <stdio.h> int main(int argc,char *argv[]) { char ch; if(argc != 2){ printf("引数が正しくありません"); exit(1); } else while(*argv[1] != '\0'){ printf("%c",*(argv[1]++) + 1); } return 0; } できた、できたと喜んでいたのですがソースをよく見ていると*(argc[1]++)の所を見てあれっと思いました。これでは文字列の二番目の要素からしか表示できないのではないのかと思ったのです。しかし動かすとちゃんと動くのです。ちゃんと1番目の要素も表示されるのです。自分としては++を消して次の行にargv[1]++を付け加えればいいと思ったのですがどうでしょうか?(←これでもちゃんと動きました)私のポインタに対する考え方が間違っているのでしょうか?よろしくお願いします。

  • コマンドとコマンドラインとコマンドラインオプションの違いを教えてください。

    ただ今Cの関数を勉強しているのですが、勉強用HPに ************************ int main(int argc, char *argv[ ]) 戻り値:int型、引数:argc, argv 後者は、コマンドライン オプションを受け付ける。 argc はオプションの数、argv はオプションの内容となる。 ************************ とあります。 そこで質問なのですが、コマンドラインオプションとは何ですか? また、 ・コマンド ・コマンドライン ・コマンドラインオプション の違い、それぞれの意味も教えてください。 マックのコマンド+s、コマンド+vみたいなものですか? それともunixのcdや./のようなものでしょうか。 わからない単語が出てくると、 そこで頭が混乱してしまい先に進めない性質なので困っています。 どうぞご教授よろしくお願いします。

  • 引数の渡し方と受け方(argv)

    メインで引数をもらい、それをパラメタチェック関数に 渡す場合、どのような形で渡せばいいですか? ※メインでargvを変数に格納しないで、 そのままargvを関数に渡す方法が分かりません。 int prm_chk(char *argv[]) { /* 処理省略 */ } int main (int argc, char *argv[]) { /* 処理省略 */ /* 以下のパラメタ(argv)の渡し方 */ if(prm_chk(*argv[]) != 0){ exit(-1); } exit(0); }

  • コマンドライン引数

    コマンドライン引数で以下のようなプログラムを実行したいのですが どうしたらいいのか分かりません!! みなさんの意見を聞かせてください(lll´Д`lll)    □☆□□□☆□□□☆□□□☆□    ☆★☆☆☆★☆☆☆★☆☆☆★☆    □☆□□□☆□□□☆□□□☆□    □☆□□□☆□□□☆□□□☆□    ☆★☆☆☆★☆☆☆★☆☆☆★☆    □☆□□□☆□□□☆□□□☆□    □☆□□□☆□□□☆□□□☆□    ☆★☆☆☆★☆☆☆★☆☆☆★☆    □☆□□□☆□□□☆□□□☆□    □☆□□□☆□□□☆□□□☆□ プログラムは途中まで作ったのですが、肝心なとこは 全く分りませんっっ #include <stdio.h> int main(int argc, char *argv[]) { int yoko, tate, i, j; if( argc < 3 ) return(1); sscanf( argv[1], "%d", &yoko ); sscanf( argv[2], "%d", &tate ); for( j = 0; j < tate; j++ ) { printf("\n"); } return(0); } /* end of pat2.c */

専門家に質問してみよう