
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の難しいところです。でもここを理解できれば、あ
とはほとんど覚えるだけの作業になるので頑張ってください(^_^)
頂いた回答をすんなり理解できるレベルになかったため、
ポインタについて、さらに詳しく参考書で勉強しておりました。
お礼が遅れて、すみません。
>C言語には文字列をそのまま関数に渡す方法がないのです。
>そこで仕方なく文字列の先頭のアドレスを渡してあげる
大変参考になりました。
頂いた回答は全体的に、大事なことが沢山詰まっており、
とても分かりやすかったです。
ありがとうございました。
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 バイトですね。
>メモリが無駄に使われてない事が解るのではないか
なるほど、勉強になりました。
参考にさせて頂きます。
ありがとうございました。
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"
という「文字列」が渡ります. どちらが「より簡単に引数を扱える」と思いますか?
文字列を、バラバラな状態で渡すために、
文字列それぞれの先頭アドレスを配列に入れて渡しているわけですね。
文字の配列と文字列の配列の違いに気付かされました。
ありがとうございます。
No.6
- 回答日時:
ポインタの説明には、確かにメモリの図があった方が分かりやすいですね。
私は参考書の図を見て、理解することができました。
参考になるページを教えて下さり、ありがとうございます。
No.5
- 回答日時:
引数 1個ずつが「文字列」 = char * で, それがたくさんあるからその配列となって char *[], ってだけなんだけどなぁ....
ちなみに関数の仮引数などでは char ** と char *[] は同じ (どちらも char ** と解釈される) ですが, 配列とポインタは本来「違うもの」です.
>引数 1個ずつが「文字列」 = char * で, それがたくさんあるからその配列となって char *[], ってだけなんだけどなぁ....
この説明は、「ポインタの配列」を理解できた後に、理解することができました。
>配列とポインタは本来「違うもの」です.
この点も勉強になりました。
ありがとうございます。
No.3
- 回答日時:
まず、文字列は
char a[10];
とかで表現しますよね?
そうするとパラメータで渡す場合は「*a」または「a[]」となります。
では、複数の文字列は
char a[5][10]
とかで表現しますよね?
そうするとパラメータで渡す場合は「**a」または「a[][]」または「*a[]」と
なります。
引数では
a.exe パラメータ1 パラメータ2
と複数のパラメータを指定したい場合があります。
そうすると複数文字列の場合になるので、「argv[]」ではダメで
「*argv[]」となります。
ちなみに「**arg」と書いても問題ないし、そう書く人もいます。
>そうするとパラメータで渡す場合は「*a」または「a[]」
すみません、この段階で既につまずいております。
ただ、これが分かれば、後はスムーズに理解できそうだと思っているのですが…。
ポインタの使用意義が私はまだよく分かっていないようです。
>「**a」または「a[][]」または「*a[]」
そもそも、これらは全て、同値なのでしょうか。
もう、この辺りはさっぱりわかりません。すみません。泣
No.2
- 回答日時:
argvの型はchar **と書かれることもあります。
まぁそんなことはさておきますが、引数が複数あった場合に(char *)[]なら単純にargvを配列参照すれば各引数にアクセスできますが、char []だとわざわざ分割して解析して、という手順が必要になります。
ということで「引数の扱いを平易にするため」ということでどうですか?
>(char *)[]なら単純にargvを配列参照すれば各引数にアクセスできますが、char []だとわざわざ分割して解析して、という手順が必要に
(char *)[]
こういった書き方もあるのでしょうか?
>配列参照すれば各引数にアクセスできます
>わざわざ分割して解析
この辺りが、おそらく本件の回答の核であるように感じましたが、
初心者の私には、上記の意味するところがよく分かりませんでした。
もし可能でしたら、それが意味する内容を、もう少し噛み砕いてご説明頂けますと嬉しいです。
回答、ありがとうございます。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# C言語 共用体について コマンドライン引数で値を2つ入力したときに、argv[2]の値をUNI u1 4 2022/04/25 20:34
- C言語・C++・C# c言語 プログラムのエラー 1 2023/02/11 20:31
- C言語・C++・C# c言語の問題の説明、各所ごとに 5 2023/07/26 11:03
- C言語・C++・C# バイナリファイルをコピーするのにかかる時間を測りたいのですが実行するとFatel error:gli 2 2022/11/03 01:10
- C言語・C++・C# c言語 コマンドライン引数 4 2023/02/09 18:47
- C言語・C++・C# Cのdoubleの浮動小数点表示について 3 2023/04/17 13:14
- C言語・C++・C# このプログラミング誰か教えてくれませんか 1 2022/06/02 15:27
- C言語・C++・C# C++プログラミングコードにポリモーフィズムを取り入れ方を教えてください。 2 2023/06/09 11:17
- C言語・C++・C# C言語のファイル入力が分かりません 2 2022/05/22 06:35
- C言語・C++・C# 並列プログラミングのπ計算について 1 2022/07/16 22:30
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
C言語のintとcharの違いってな...
-
配列やポインタに文字列を設定...
-
ポインタとcharについて
-
36進数
-
strcpy関数で文字型変数へのポ...
-
fscanfの書式について
-
警告 : 問題のあるポインタの変換
-
エクセルのMID関数は、C言語では?
-
DWORDとcharの変換
-
コマンドライン引数
-
コマンドライン引数 *argv[]は...
-
文字列の反転
-
しつこい様ですが、再度ポイン...
-
2次元配列の文字"列"の初期化方法
-
ポインタ配列
-
WM_CHAR or WM_KEYDOWN の「wPa...
-
strcat関数を自作したいです
-
C言語 配列の長さの上限
-
WSH(VBS)でJSONの文字列を読み...
-
C++ メンバイニシャライザリス...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
CStringからchar*への型変換に...
-
char*を初期化したいのですが
-
C言語にて構造体のメンバがNULL...
-
C言語のintとcharの違いってな...
-
DWORDとcharの変換
-
C++17で、unsigned char * 配列...
-
char 文字列型 の表現範囲が-12...
-
new charとnew char[N]の違いは?
-
char型にint型の数値を代入する。
-
動的メモリの初期化方法について。
-
小数点入りの文字列をfloat型に...
-
文字型配列に格納した空白の切捨て
-
fstream型オブジェクトを関数の...
-
C++Builder 2009 テキスト...
-
文字列の途中から途中までを抽出
-
C言語の文字リテラル中の16進文...
-
エクセルのMID関数は、C言語では?
-
文字列のswap
-
void型へのポインタ
-
VC++ char[10]へのCString値の代入
おすすめ情報