C言語におけるポインタのポインタ(ダブルポインタ)

こんにちは、夢を抱くプログラマーの皆さん!今日は、ポインタの世界についての興味深い旅に出かけましょう。具体的には、ポインタのポインタについてです。今、あなたが何を考えているかわかっていますか?"ポインタ?ダブルポインタ?これは悪夢のようです!" と。でも心配しないで、楽しくて理解しやすいように説明できるという約束します。だから、お気に入りの飲み物を持って、リラックスして、一緒に飛び込みましょう!

C - Pointer to Pointer

C言語におけるダブルポインタとは?

宝探しを想像してみてください。あなたは宝の場所を示す地図(ポインタと呼びましょう)を持っていますが、その中には別の地図(さらに別のポインタ)が入っていて、それが実際の宝を示しています。それがダブルポインタの基本的な概念です -別のポインタを指すポインタです。

Cプログラミングでは、ダブルポインタはその名の通り、ポインタのポインタです。別のポインタのアドレスを格納する変数です。最初は少し混乱するかもしれませんが、心配しないで、ステップバイステップで説明します。

ポインタのポインタの宣言

ダブルポインタを宣言する方法から始めましょう。構文は非常に簡単です:

int **ptr;

ここで、ptrは整数のポインタのポインタです。最初のアスタリスク * はそれをポインタにし、二つ目の * はそれをポインタのポインタにします。

ポインタのポインタ(ダブルポインタ)の例

この概念をよりよく理解するために、簡単な例を見てみましょう:

#include <stdio.h>

int main() {
int x = 5;
int *p = &x;
int **pp = &p;

printf("xの値: %d\n", x);
printf("pを使ってのxの値: %d\n", *p);
printf("ppを使ってのxの値: %d\n", **pp);

return 0;
}

出力:

xの値: 5
pを使ってのxの値: 5
ppを使ってのxの値: 5

これを分解して見ていきましょう:

  1. 整数 x を宣言し、5で初期化します。
  2. x を指すポインタ p を作成します。
  3. ポインタ p を指すダブルポインタ pp を作成します。
  4. 三つの異なる方法で x の値を出力します:
  • 直接 x を使って
  • 単一ポインタ p を使って(一度デリファレンスする *p
  • ダブルポインタ pp を使って(二度デリファレンスする **pp

すべての方法で同じ値を得ます:5。それは異なる地図を使って宝を見つけるようなものです!

C言語における通常のポインタの動作

ダブルポインタについて深く掘り下げる前に、通常のポインタの動作について簡単に復習しましょう:

int y = 10;
int *q = &y;

printf("yの値: %d\n", y);
printf("yのアドレス: %p\n", (void*)&y);
printf("qの値: %p\n", (void*)q);
printf("qが指す値: %d\n", *q);

出力:

yの値: 10
yのアドレス: 0x7ffd5e8e9f44
qの値: 0x7ffd5e8e9f44
qが指す値: 10

ここで、qy のアドレスを格納するポインタです。*q を使うと、そのアドレスに格納された値にアクセスします。

ダブルポインタの動作

では、これをダブルポインタに拡張してみましょう:

int z = 15;
int *r = &z;
int **rr = &r;

printf("zの値: %d\n", z);
printf("zのアドレス: %p\n", (void*)&z);
printf("rの値: %p\n", (void*)r);
printf("rのアドレス: %p\n", (void*)&r);
printf("rrの値: %p\n", (void*)rr);
printf("rが指す値: %d\n", *r);
printf("rrが指す値: %p\n", (void*)*rr);
printf("rrが指す指す値: %d\n", **rr);

出力:

zの値: 15
zのアドレス: 0x7ffd5e8e9f48
rの値: 0x7ffd5e8e9f48
rのアドレス: 0x7ffd5e8e9f50
rrの値: 0x7ffd5e8e9f50
rが指す値: 15
rrが指す値: 0x7ffd5e8e9f48
rrが指す指す値: 15

これは少しやっかいですが、以下のように分解して見ていきましょう:

  1. z は値が15の整数です。
  2. rz のアドレスを格納するポインタです。
  3. rrr を指すダブルポインタです。
  4. *rz の値(15)を得ます。
  5. *rrr の値(z のアドレス)を得ます。
  6. **rrz の値(15)を得ます。

rrr を指し、rz を指すと考えると、**rr は "最初のポインタを追う、次のポインタを追う、そしてそこにある値を与えてくれ" という意味です。

ダブルポインタは通常のポインタと同様に動作します

ここにちょっとした秘密があります:ダブルポインタはただのポインタですが、通常のポインタが整数や浮動小数点数を指すのに対して、別のポインタを指すことができます。これは、通常のポインタと同じことをダブルポインタでもできることを意味します。

例えば、ダブルポインタを配列とともに使用することができます:

int main() {
char *fruits[] = {"Apple", "Banana", "Cherry"};
char **ptr = fruits;

for(int i = 0; i < 3; i++) {
printf("%s\n", *ptr);
ptr++;
}

return 0;
}

出力:

Apple
Banana
Cherry

この例では、fruits は文字列を指すポインタの配列です(各々が文字列を指しています)そして、ptr は文字のポインタのポインタです(fruits の要素に指すことができます)。

C言語における複数レベルのポインタ(トリプルポインタは可能でしょうか?)

はい、トリプルポインタ、クアドUPLEポインタなどもあります!理論的には、どれだけ多くのインダイレクションを持つことができるかには制限はありません。しかし、実際にはダブルポインタ以上のレベルのインダイレクションを見ることはまれです。

以下はトリプルポインタの例です:

int x = 5;
int *p = &x;
int **pp = &p;
int ***ppp = &pp;

printf("xの値: %d\n", ***ppp);

出力:

xの値: 5

しかし、できるだけあっても、すべきだとは限りません。複数のレベルのインダイレクションは、コードの可読性と保守性を低下させる可能性があります。古いプログラミングの格言によれば、「コンピュータサイエンスのすべての問題は、さらなるインダイレクションレベルで解決できます...ただし、インダイレクションレベルが多すぎる問題を除きます!」。

結論

おめでとうございます!あなたは今、C言語におけるダブルポインタの難しい海を乗り越えました。プログラミングの多くの概念と同様に、ポインタのポインタは最初は混乱するかもしれませんが、練習すれば自然になります。

以下は、私たちがカバーした主要なポイントをまとめたテーブルです:

概念 構文 説明
通常のポインタ int *p; 整数を指す
ダブルポインタ int **pp; 整数のポインタを指す
デリファレンス *p p が指す値にアクセス
ダブルデリファレンス **pp pp が指すポインタが指す値にアクセス
アドレス演算子 &x x のアドレスを取得

続けて練習し、興味深いことを探求し、憶えておくのをお勧めします。まだ初心者のエキスパートがいます。幸せなコーディングを!

Credits: Image by storyset