みんなの「教えて(疑問・質問)」にみんなで「答える」Q&Aコミュニティ

こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

ポインタについての質問など

(1)
プログラム引数を取る時の記述ですが….
(int argc, char *argv[])
(int argc, char **argv)
本によって記述がまちまちなんです.
これらはどう違うのでしょうか?
特に後者の解釈の仕方がいまいち分からないので教えてください.
配列になってないように見えるのですが….

(2)
後者の記述(int argc, char **argv)で書かれたプログラムで
./program.exe okwave
と引数を取ったとき,okwaveのoからeまでをfor文やwhile文でたどって何らかの処理をしたいのですが,どうすればいいのでしょうか?

(3)
ポインタには関係ありませんが,Windowsでncursesは使えないのでしょうか?

投稿日時 - 2006-05-20 01:26:01

QNo.2162531

困ってます

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

> *argv[]の図は本にありましたが,**argvを図式化すると,同じ図になるのでしょうか…?

意味が全く同じですから、当然図式化しても同じです。
*argv[] は便宜的な関数の仮引数の書き方であり、本来の意味は、
**argv である、と考えるとわかりやすいと思います。
関数内で、「char **argv」で宣言したときと同じです。
argvはポインタのポインタであり、配列ではありません。
ちなみに、関数の仮引数なら、argv[][]という書き方もできるは
ずです。

> p=argv[1]で指定できるんですね.

その通りです。

> argv[1]と書くのはargv[0]がコマンド自体を指すからですよね.

その通りです。

> *pはargv[1]が指す文字列の先頭を指すということですね?

「先頭をさす」というよりは、先頭の文字そのものです。
*p の型は「ポインタ」ではなく、「char」です。

> while(*p)ですが,while(*p != '\0')ということですか?

その通りです。

> 自分は,pを使わずに,argvそのものをインクリメントして参照しようとしていました.
> *の数が違うのか,ここでエラーが出てなかなか進まなかったんです.

argv++;
のような書き方ですか?
これだと、argv がさすもの(つまり、*argv)が、0番目の引数、1番目の引数、2番目
引数と変わっていきます。
argv[1]++;
ならば目的は果たせますね。

投稿日時 - 2006-05-20 09:57:07

お礼

ありがとうございます.
新たな発見があってうれしいです.

意味が同じなので図も同じということですね.
なるほど・・・,**argvが本来の意味であると….
今までの回答から,「char **argv」と「char *argv[]」のどちらで書いてあっても,同じような使い方をしても問題はないんですね?

*pは間接参照だから,文字そのものなんですね.
*p=a;としたら,そこの字が変わると….

argvですが,そういう感じです.
argv++,*argv++,*(argv++),*(*argv++)など考えたんですが,どれが何を意味しているのか混乱してきました.
もしかしたら,無効なものがあるかも….
argv[1]でやると分かりやすいですが,argvで理解したいです.

argv[1]++という書き方も出来るんですね.
ですが,これでプログラムを書くとビープ音の荒らしが…(汗)
→while(argv[1]){argv[1]++;}
こうするとOKでした.
→while(*argv[1]){*argv[1]++;}

投稿日時 - 2006-05-20 22:29:18

ANo.4

このQ&Aは役に立ちましたか?

3人が「このQ&Aが役に立った」と投票しています

回答(11)

ANo.11

>>*1[argv]++
>は、ダメだと思います。
>++で進めるべきポインタ(の実体)が無いような気がします。
>例え、どっかの処理系で動いたとしても、別の処理系で動くとは限りません。
すいません、これって大丈夫ですね。
++がどこにかかるのか混乱してしまいました。
結局#8様が言われるように
#8>*argv[1]++ のところを、同じ意味の (*(argv+1)) におきかえれば
というように同じ意味になるわけですもんね。
混乱させるようなことを言ってすみませんでした。

投稿日時 - 2006-05-21 03:52:14

お礼

そうですか.
つまり,
argv[1]++ ⇔ (*(argv+1))++
1[argv]++ ⇔ (*(1+argv))++
ですね.
表記は分かりづらいですが,面白いですね.

投稿日時 - 2006-05-21 13:13:50

ANo.10

>*1[argv]はよくても,*1[argv]++はダメですか….用心します.
#8>printf("%c\n",*(*(argv+1))++);
#8さんの書き方はOKですね。
でも、正直気持ち悪くてこのようには(私は)書きません。
まあ、気持ちの上での話ですが、
渡された領域は、コマンドライン引数ですから
どっちかというと、定数的な領域です。
ポインタの部分で一度しか使わないのだとしても破壊的に使うのはとても気が進みません。

>argv[1][i]は2次元配列のように見えますが….
argv[1] がコマンドライン引数の文字列の先頭を指しているということなので、
argv[1][i] は、その文字列のi 番目の文字を取り出すということになります。
p=argv[1] として
p[i] とみればわかりがいいですか?
ここでも(サイズが不定長なので)2次元配列としてみるより[ ] は、演算子としてみるのがいいかもしれません。
p[i] → *(p + i)

投稿日時 - 2006-05-21 03:38:41

お礼

ありがとうございます.
普通は好ましくない表記なんですね.
おかげで簡略化して書く意味がわかりました.

argv[1][i]は,argv[1]のi番目ということで,イメージがしやすいですね.
pに置き換えると簡略化されて扱いやすいですね.
[]を演算子としてみるというのも,大変参考になりました.

色々な考え方,とらえ方が参考になりました.
より確実な理解につながるのでとても感謝しています.

投稿日時 - 2006-05-21 12:42:39

ANo.9

ごめんなさい、間違えました。
>*argv[1]++ のところを、同じ意味の (*(argv+1)) におきかえれば
argv[1] のところを、同じ意味の (*(argv+1)) におきかえれば

投稿日時 - 2006-05-21 02:59:33

お礼

了解しました.

投稿日時 - 2006-05-21 12:33:54

ANo.8

こうなります。
while(**(argv+1)){
printf("%c\n",*(*(argv+1))++);
}

*argv[1]++ のところを、同じ意味の (*(argv+1)) におきかえれば
OKです。かっこがついてるのは評価順序の問題です。

とはいっても普通はこんな書き方はしませんが・・・
正直、わかりにくいです。
私が書いても多分、BLUEPIXYと同じような感じになります。
なぜだかうまく説明できませんが、
多分一般的には配列の書き方の方がわかりやすいのでしょうか。

投稿日時 - 2006-05-21 02:57:13

お礼

ありがとうございます.

ポインタのみだと確かに分かりにくいですね.
一方,配列表記だと分かりやすいですね.
こちらとしても,*argv[1]などと書けば,おかげで分かるようになりました.
でも,ポインタを理解するために質問させていただきました.
最初に与えられたポインタのみでどう表現するのか,どう動いているのか,というのが知りたかったんです.
pなどに置き換えることで,表記を簡略化していたんですね.

大体の形は分かったので,それをイメージしつつ分かりやすい書き方をしていこうと思います.

投稿日時 - 2006-05-21 12:33:07

ANo.7

#1>**argvですが,プログラム引数を与えて初めて配列だと明らかになるということですか?
**argv は、あくまでポインタのポインタであって、配列として使うかどうかは別の問題です。
逆に
*argv[] は、引数が配列であることを意味していて、例え、サイズが1の配列でも、それが配列であるということをプログラマが意識しているというような意味合いですね。

>「char **argv」と「char *argv[]」のどちらで書いてあっても,同じような使い方をしても問題はないんですね?
Cの規格的にもどっちで書いても良いことになっています。

>*1[argv]++
は、ダメだと思います。
++で進めるべきポインタ(の実体)が無いような気がします。
例え、どっかの処理系で動いたとしても、別の処理系で動くとは限りません。
>printf("%c",*p++)をpなしで書くとすればどうなるんでしょうか?
int i=0;

while(argv[1][i]){
printf("%c\n", argv[1][i++]);
}
のように添字を変化させてアクセスするということになると思います。

投稿日時 - 2006-05-21 02:21:09

お礼

ありがとうございます.
詳しい説明など,いろいろ感謝しています.

*1[argv]はよくても,*1[argv]++はダメですか….用心します.

argv[1][i]は2次元配列のように見えますが….
*の数が[]の数に影響しているようなので,そう書けるということですね.
*(argv+i) ⇔ argv[i]
*argv ⇔ argv[]
**argv ⇔ argv[][]

投稿日時 - 2006-05-21 02:38:57

ANo.6

>今までの回答から,「char **argv」と「char *argv[]」のどちらで書いてあっても,同じような使い方をしても問題はないんですね?
当然ながら、mainの中身は全く同じ書き方ができます。

> *pは間接参照だから,文字そのものなんですね.
間接参照だから、ではなくて、argvの型が char** で、
pの型が char* だからです。
*argv は間接参照ですが、型は char ではないですよね?

> *p=a;としたら,そこの字が変わると….
その通りです。

> argv++,*argv++,*(argv++),*(*argv++)など考えたんですが,どれが何を意味しているのか混乱してきました.
> もしかしたら,無効なものがあるかも….
混乱したときは、図をイメージして考えることです。後は、それぞれ
の演算子の評価順序を正確に把握することです。
(実は私もちょっと混乱しまして、調べました。単項演算子は、原則右から左に
 評価するんですね。)
・argv++
 ポインタのポインタを1つ進める。初めに0番目の引数をさしていた argv は++を
 1回行うと、次は1番目の引数をさすようになり、2番目、3番目・・・
 となっていきます。
・*argv++
 *argv を評価した後、argv++ を実行。
・*(argv++)
 *argv++ と同じ。
・*(*argv++)
 **argv を評価した後、argv++ を実行。

私は*と++を一行で書く書き方は好きではないのですが、まあ、好みの
問題ですかね。自分が使わない書き方なので、正直ちょっと混乱して
しまいました。上記の結果は Visual C++ で確認しましたので、間違い
ありません。

>argv[1]でやると分かりやすいですが,argvで理解したいです.
純粋にポインタらしい書き方を理解したいということですか?
ただ、argv の中身を直接変えないで、他の変数に置き換えてから
操作した方がわかりやすいかもしれませんね。多分その方が他人に
も見やすいです。

char *p;
p = *(argv + 1);
とか、

char **pp;
pp = argv + 1;
とか。

>→while(argv[1]){argv[1]++;}
そうではなくて、私のイメージはこうです。

while(*argv[1]) {
  <何かの処理>
  argv[1]++;⇒ここの頭に"*"をつけることは意味がありません。
}

ちなみに、
while(*argv[1]){
printf("%c\n",*argv[1]++);
}
は正しいように思いますが、何がうまくいかないのでしょうか?
少なくとも、Visual C++ ではうまくいきますよ。


もし将来C言語を仕事に使う可能性があるなら、以下の本の一読をおすすめします。
ポインタの理解も、その他の理解も深まると思います。

Cプログラミング専門課程
http://www.amazon.co.jp/exec/obidos/ASIN/4774100900/qid%3D1148132399/250-9131237-4185014

投稿日時 - 2006-05-21 00:26:18

お礼

ありがとうございます.
詳しい説明でかなり分かってきました.(少なくとも質問前よりは)

ポインタらしい書き方を理解したいと思っています.
が,分かりやすいほうが好ましいということですね.
最初のpを使う例で,置き換えるとやりやすくなることはわかりました.

コンパイラはBorland C++Compilerを使っています.もしくはCygwinのgccです.
while(*argv[1]){~}は,きちんと動きました.
これを配列の表記なしに再現しようとして詰まりました.

argv[1]は*(argv+1)と同じですよね.ということは,
while(*argv[1])はwhile(**(argv+1))で書き直せるということですね?
ですが,*argv[1]++と同じものが書けません.
**(argv+1)++は無効のようですし….
argv[1]++をまねして,*(argv+1)++も無理ですし….

while(**(argv+1)){
printf("%c\n",**(argv+1));
/*インクリメントが分からない*/
/*「argv[1]++;」だといけるが…*/
}
こんな状況です.

長引いてすみません.

投稿日時 - 2006-05-21 02:21:43

ANo.5

#1>*argv[]の図は本にありましたが,**argvを図式化すると,同じ図になるのでしょうか…?
*argv[] は、配列であることが明かですが、
**argv は、明かではありません。

main に コマンドライン引数が渡されている様子を図式化したとしたら当然同じになるでしょう。
Cでは配列とポインタはほぼ同等に扱えるのでほぼ同じと考えていいです。
あと、
変数の定義
(引数でないところで char *argv[]={"a","b","c"};等と書く)としては別ですが、
関数の引数で、argv[] と*argv は同じと考えていいでしょう。

#1>p=argv[1]で指定できるんですね.
Cにおいては、[ ] は、構文というより演算子なのです。
p=argv[1] は、
p=1[argv] と書いても同じです。(普通こんな書き方はしませんが・)
どちらも
p=*(argv + 1)
の(演算を行う)意味になります。(つまり、argv + 1 が 1 + argv でも同じ)

#1>*pはargv[1]が指す文字列の先頭を指すということですね?
p はargv[1] が指す文字列の先頭を指し
*p は、その中身(char)の文字

>while(*p)ですが,while(*p != '\0')ということですか?
そうです。
>pを使わずに,argvそのものをインクリメントして参照しようとしていました。
argv ポインタを変更してもいいなら
p=*(++argv);
とすれば
p=argv[1];
と同じです。
注意するのは、argv のインクリメントは、
argv[0] → argv[1] → argv[2]
であることです。

投稿日時 - 2006-05-20 11:35:57

お礼

ありがとうございます.
**argvですが,プログラム引数を与えて初めて配列だと明らかになるということですか?

[]は演算子ですか….
そんな書き方が出来るとは知りませんでした.
試しに書いてみました.
#include <stdio.h>
int main(int argc, char **argv){
if(argc!=2){
printf("error!\n");
return -1;
}
while(*1[argv]){
printf("%c\n",*1[argv]++);
}
return 0;
}
本当に動いたので感動しました.

ポインタに対して「指す」という言葉を用いるんですね.
pはポインタなので,先頭を「指す」,つまりアドレスですよね.
*pは「間接参照」なので,文字だということですよね.

p=*(++argv)で,p=argv[1]ですか.
printf("%c",*p++)をpなしで書くとすればどうなるんでしょうか?
*p++は,**(argv++)ですか?*(*argv++)ですか?

現状ですが,
while(*argv[1]){
printf("%c\n",*argv[1]++);
}
をpや[1]を使わずに書こうとして失敗しています.
プログラムが止まらなかったり,エラーが出たり….

投稿日時 - 2006-05-20 23:30:41

ANo.3

cygwin ncurses
で検索したらいろいろ出てきますから、cygwinならば多分使えますね。

ただ私もやったことはないので・・・

投稿日時 - 2006-05-20 09:38:10

お礼

そうですか….
試してみます.

投稿日時 - 2006-05-20 21:36:07

ANo.2

(1)関数の仮引数として書く場合、意味は全く同じです。
 配列とポインタの関係について勉強してください。
 参考:
   http://www.kouno.jp/home/c_faq/c6.html#0
(3)多分使えないのでは?

投稿日時 - 2006-05-20 02:26:44

お礼

ありがとうございます.
今勉強中で,かつ混乱中です.
ほかの人の考え,とらえ方を参考にすれば新たな発見があるかと思ったのですが….軽くHP参照で流された(汗)
でも,Q&A方式のページでよかったです.わりと近い疑問がありました.

>ncurses
多分,使えないですか….
ざっとHPを回ったのですが,Windowsでncursesが云々と書いてあるページがありませんでした.
なので困っています.多分…ということなので,はっきりとしたことが知りたいです.
Cygwinでも無理ですか…?

投稿日時 - 2006-05-20 08:22:39

ANo.1

(1)
*argv[] は、ポインタの配列
**argv は、ポインタのポインタ

配列をポインタでアクセスすることを考えるとどちらでもいいです。

(2) 例えばこんな感じ
#include <stdio.h>

int main(int argc, char **argv){

char *p;

if(argc != 2){
fprintf(stderr, "引数が1つ必要です\n");
return -1;
}

p=argv[1];//**argv でもこのように使える

while(*p){
printf("%c\n", *p++);//一文字ずつたどる
}

return 0;
}
(3) 寡聞にして知りません。

投稿日時 - 2006-05-20 02:13:41

お礼

ありがとうございます.
どちらでもいいんですね.
図式化したときに違いが出てくるのかと思ったんです.
*argv[]の図は本にありましたが,**argvを図式化すると,同じ図になるのでしょうか…?

p=argv[1]で指定できるんですね.
勉強中なので,確認がてらに書かせてもらいます….

argv[1]と書くのはargv[0]がコマンド自体を指すからですよね.
*pはargv[1]が指す文字列の先頭を指すということですね?
while(*p)ですが,while(*p != '\0')ということですか?
自分は,pを使わずに,argvそのものをインクリメントして参照しようとしていました.
*の数が違うのか,ここでエラーが出てなかなか進まなかったんです.

またまたすみません….

投稿日時 - 2006-05-20 08:15:41