アプリ版:「スタンプのみでお礼する」機能のリリースについて

C言語初心者です。

コマンドライン引数、
int main(int argc, char *argv[])

というのを最近勉強しましたが、引数2番目がポインタになっている理由について、
どなたか教えて下さい。
そういう仕様なんだから、それに従いましょう、ということでしょうか?

int main(int argc, char argv[]) では、ダメなのでしょうか?

このポインタでの引数渡しについて、
なんらかの納得のいく考え方をご存知の方がいらしたら、教えて下さい。
宜しくお願い致します。

A 回答 (9件)

関数への引数の渡し方には大きく分けて以下の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の難しいところです。でもここを理解できれば、あ
とはほとんど覚えるだけの作業になるので頑張ってください(^_^)
    • good
    • 0
この回答へのお礼

頂いた回答をすんなり理解できるレベルになかったため、
ポインタについて、さらに詳しく参考書で勉強しておりました。
お礼が遅れて、すみません。

>C言語には文字列をそのまま関数に渡す方法がないのです。
>そこで仕方なく文字列の先頭のアドレスを渡してあげる

大変参考になりました。
頂いた回答は全体的に、大事なことが沢山詰まっており、
とても分かりやすかったです。
ありがとうございました。

お礼日時:2010/11/15 00:32

例えば、下の様に動かして見るとどうなるか考えて見てください。



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 バイトですね。
    • good
    • 0
この回答へのお礼

>メモリが無駄に使われてない事が解るのではないか

なるほど、勉強になりました。
参考にさせて頂きます。
ありがとうございました。

お礼日時:2010/11/15 00:53

#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"
という「文字列」が渡ります. どちらが「より簡単に引数を扱える」と思いますか?
    • good
    • 0
この回答へのお礼

文字列を、バラバラな状態で渡すために、
文字列それぞれの先頭アドレスを配列に入れて渡しているわけですね。

文字の配列と文字列の配列の違いに気付かされました。
ありがとうございます。

お礼日時:2010/11/15 00:46

>引数2番目がポインタになっている



ポインタではなく、「ポインタの配列」です。
Tacosanさんの回答のとおり。
    • good
    • 0
この回答へのお礼

>ポインタではなく、「ポインタの配列」

そうでしたね。
質問当初は、よく分かっておりませんでした。
今はおかげさまで、だいぶ分かるようになりました。
ありがとうございます。

お礼日時:2010/11/15 00:41

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



参考URL:http://ysserve.int-univ.com/Lecture/c2/e_04-03.h …
    • good
    • 0
この回答へのお礼

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

お礼日時:2010/11/15 00:38

引数 1個ずつが「文字列」 = char * で, それがたくさんあるからその配列となって char *[], ってだけなんだけどなぁ....


ちなみに関数の仮引数などでは char ** と char *[] は同じ (どちらも char ** と解釈される) ですが, 配列とポインタは本来「違うもの」です.
    • good
    • 0
この回答へのお礼

>引数 1個ずつが「文字列」 = char * で, それがたくさんあるからその配列となって char *[], ってだけなんだけどなぁ....

この説明は、「ポインタの配列」を理解できた後に、理解することができました。

>配列とポインタは本来「違うもの」です.

この点も勉強になりました。
ありがとうございます。

お礼日時:2010/11/15 00:35

まず、文字列は


char a[10];
とかで表現しますよね?

そうするとパラメータで渡す場合は「*a」または「a[]」となります。

では、複数の文字列は
char a[5][10]
とかで表現しますよね?

そうするとパラメータで渡す場合は「**a」または「a[][]」または「*a[]」と
なります。

引数では
a.exe パラメータ1 パラメータ2
と複数のパラメータを指定したい場合があります。

そうすると複数文字列の場合になるので、「argv[]」ではダメで
「*argv[]」となります。
ちなみに「**arg」と書いても問題ないし、そう書く人もいます。
    • good
    • 0
この回答へのお礼

>そうするとパラメータで渡す場合は「*a」または「a[]」

すみません、この段階で既につまずいております。
ただ、これが分かれば、後はスムーズに理解できそうだと思っているのですが…。

ポインタの使用意義が私はまだよく分かっていないようです。

>「**a」または「a[][]」または「*a[]」

そもそも、これらは全て、同値なのでしょうか。
もう、この辺りはさっぱりわかりません。すみません。泣

お礼日時:2010/11/12 20:35

argvの型はchar **と書かれることもあります。



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

ということで「引数の扱いを平易にするため」ということでどうですか?
    • good
    • 0
この回答へのお礼

>(char *)[]なら単純にargvを配列参照すれば各引数にアクセスできますが、char []だとわざわざ分割して解析して、という手順が必要に

(char *)[]

こういった書き方もあるのでしょうか?

>配列参照すれば各引数にアクセスできます
>わざわざ分割して解析

この辺りが、おそらく本件の回答の核であるように感じましたが、
初心者の私には、上記の意味するところがよく分かりませんでした。
もし可能でしたら、それが意味する内容を、もう少し噛み砕いてご説明頂けますと嬉しいです。
回答、ありがとうございます。

お礼日時:2010/11/12 20:27

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

    • good
    • 0
この回答へのお礼

すみません、まだまだ初心者であるため、
おっしゃることの意味がよく分かりませんでした。

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

お礼日時:2010/11/12 20:21

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!


このQ&Aを見た人がよく見るQ&A