• ベストアンサー
※ ChatGPTを利用し、要約された質問です(原文:ポインタ変数とポインタのポインタ)

ポインタ変数とポインタのポインタ

このQ&Aのポイント
  • ポインタ変数とポインタのポインタについての疑問があります。
  • 特に、ポインタ配列とポインタのポインタの違いがわかりません。
  • プログラムの実例を見ながら、理解を深めたいです。

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

  • ベストアンサー
noname#208507
noname#208507
回答No.3

おそらく、実際に試してみるのが手っ取り早いでしょう。 > girlはどのように配列の終わりを理解しているのでしょうか? コンパイラが配列サイズを知っています。下の test2.c をコンパイルしてみれば、コンパイラがサイズを知らない配列は sizeof できないことが分かると思います。 > 文字列のアドレスを示すgirlという名の1つのポインタを渡すと、 > pという名のポインタのポインタで受け取るというのも、 > よくわからなくなっています。 質問者さんご自身が、質問で引用されていた「関数でポインタ配列を受け取るとき」という部分が肝心です。 配列とポインタを、必ずしも同じように扱えるわけではありません。同じように扱える例を先に学ばれてしまったのが混乱の元になっていると思います。下の test1.cとtest3.c、および test1.cとtest4.c を分割コンパイル/リンクしたプログラムを実行して、比較してみてください。 そして test5.c をコンパイル/実行してみれば、関数の仮引数には実引数そのものではなく、コピーが渡されていることが分かると思います。ポインタの配列でも、ポインタのポインタであっても。「関数でポインタ配列を受け取るとき」char *p[]はchar **pとしてもよいのはこのためです。 // ----- test1.c ----- // (要 分割コンパイル) char *boy[] = {"Dahl", "Dijkstra", "Hoare"}; // ----- test2.c ----- #include <stdio.h> int main(void){ char *girl[] = {"Arica","Candy","Lisa"}; extern char *boy[]; printf("%zu\n", sizeof(girl)); // サイズは既知 printf("%zu\n", sizeof(boy)); // サイズ不明(コンパイルエラー) return 0; } // ----- test3.c ----- // 配列に正常にアクセスするケース #include <stdio.h> int main(void){ extern char *boy[]; printf("%p\n", boy); // 配列の正しいアドレス printf("%p\n", &boy); // 上と同一のアドレス printf("%s\n", boy[0]); return 0; } // ----- test4.c ----- // 配列をポインタと同様に扱うと問題を起こすケース #include <stdio.h> int main(void){ extern char **boy; printf("%p\n", &boy); // ポインタ自身のアドレスと見なされる printf("%p\n", boy); // 余分な評価で、不正なアドレスに printf("%s\n", boy[0]); // メモリアクセス・エラー return 0; } // ----- test5.c ----- // 配列、ポインタを関数の引数にした場合 #include <stdio.h> char *girl[] = {"Arica","Candy","Lisa"}; void disp(char *array[], char **pointer){ printf("%p\n", array); // 配列の正しいアドレス printf("%p\n", &array); // 仮引数 array のアドレス printf("%p\n", pointer); // 配列の正しいアドレス printf("%p\n", &pointer); // 仮引数 pointer のアドレス printf("%s %s\n", array[0], pointer[0]); } int main(void){ disp(girl, girl); return 0; }

pipopipoid
質問者

お礼

>コンパイラが配列サイズを知っています。 ありがとうございます。その事実は非常に考えさせられます。配列はメモリ上にあるときサイズの情報を持っていない->ない、という図式で考えていたのでコンパイラが関係しているとは思いませんでした。 test4.c extern char **boy; は関数以外が*boy[]を受け取るのに**boyでは無理だという説明のために使ったため、おかしく感じて当たり前ですよね? test.5.cはある程度の理解ができるようになるましたが、やはり理解できない部分があるので図で説明しようと思います。 char *girl[] = {"Arica","Candy","Lisa"};で *girl[0]= "Arica\0" *girl[1]= "Candy\0" *girl[2]= "Lisa\0" を宣言し、それぞれの文字列の最初のポインタがgirl[0],girl[1],girl[2]に入ると考えています。 すなわち、 *girl[0]= "Arica\0" ↑girl[0] *girl[1]= "Candy\0" ↑girl[1] *girl[2]= "Lisa\0" ↑girl[2] と考えています。(書いた後気づきましたが、空白が詰められますね。girl[0]girl[1],girl[2]はそれぞれの文字列の最初のアドレスを指し示しています。) この図で考える限り、girl[0],girl[1],girl[2]は連続しておらず、 **pointerで受け取った場合、girlの最初のアドレスを受け取ってもgirl[1],girl[2]は離れているため推定することができないのではないかと思っています。 これが*array[]で受け取った場合であっても、アドレスをarrayという配列で受け取る、と考えれば理解できないこともないですが、girlは1つのポインタしか指していないので、やはりこれもおかしい、となります。

pipopipoid
質問者

補足

すいません、お礼を書いた後に書き込んでいますが、girl[0],girl[1],girl[2]は連続していますね・・・。 図に引っ張られすぎてこんがらがっていました。 考えていたら、だいぶ納得できたように思います。

すると、全ての回答が全文表示されます。

その他の回答 (2)

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

あ~, 「最初のアドレスを指すポインタ」って表現 (より狭くは「アドレスを指す」のところ) が微妙にあやしいのか.... 「最初の要素を指すポインタ」ならいいんだけど. C において配列名は (たいていの場合に) 「その配列の先頭要素へのアドレス」に変換される. これは「int の配列」だろうと「char * の配列」だろうと同じこと. で元の質問の文章からおかしな点をいくつか取り出すと ・「*girl[0]はAricaという文字列を直接指し示している」: そんなことはない... というか, 「直接指し示している」ってどんな状態? ・「disp側が受け取ったのは*girl[]であり、いくつかのポインタの配列ですが」: C では「配列を受け取る」ことはできない. ポインタなら受け取れる. だからこそ「char *p[]はchar **pとしてもよい」んだけど... いや, 本当は「char **p は char *p[] としてもよい」の方が正しいか. ・「文字列のアドレスを示すgirlという名の1つのポインタを渡すと」: 「文字列のアドレスを示す」とは, どういうことでしょうか? くらいは挙げられるかな. ちなみにですが, #1 のプログラムで「girlはどのように配列の終わりを理解しているのでしょうか」という疑問は持ちませんでしたか? 疑問ではないというなら, 説明してみてください. あと「前半部分が理解できないままです」の「前半部分」ってどの部分でしょうか.

pipopipoid
質問者

お礼

ちなみにですが, #1 のプログラムで「girlはどのように配列の終わりを理解しているのでしょうか」という疑問は持ちませんでしたか? 疑問ではないというなら, 説明してみてください. といいますが、 質問文中に >girlはどのように配列の終わりを理解しているのでしょうか? (配列の要素数を渡していない点が不思議です。) と書いているのですが読み落とされているのでしょうか? すみません、きちんと質問文を読んでくださっているのでしょうか?わからないから質問しているのに○○って知ってる?と同じこと聞かれると、非常にかみ合っていない感覚に陥ります。

すると、全ての回答が全文表示されます。
  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.1

まず「ポインタのポインタ」を「ある変数を指し示すアドレスのアドレス」と理解しちゃったところが間違い. 「××のポインタ」といえば, それは「××という変数のアドレス (を保持する変数)」という意味. つまり「ポインタのポインタ」は「ポインタ変数のアドレス (を保持する変数)」ということ. 後半については, そもそも配列とポインタに関する理解が足りていないんだと思う. 例えば #include <stdio.h> void disp (int p[],int n){ int i; for (i= 1;i<n;i++){ printf("%d\n",p[i]); } } int main(void){ int girl[] = {5, 1002, 79}; disp (girl,sizeof(girl)/sizeof(girl[0])); return 0; } だったらわかる?

pipopipoid
質問者

お礼

ある変数を指し示すアドレスのアドレス はポインタのポインタのポインタと勘違いしていました。 ポインタのポインタはアドレスを記憶する変数であるという点は大丈夫になったと思います。 しかし前半部分が理解できないままです。 例に示されたプログラムは理解できます。

すると、全ての回答が全文表示されます。
このQ&Aのポイント
  • 請求書についての質問です。
  • 貴社と契約をしていないのに、請求の葉書が届いたという状況について相談させてください。
  • 特に「ISPぷらら」についての問題です。
回答を見る

専門家に質問してみよう