C言語のポインタのポインタを解説
- 作成日: 2021-04-26
- 更新日: 2023-12-26
- カテゴリ: C言語
C言語のポインタのポインタ
C言語ではポインタ変数のアドレスを別のポインタ変数に保存することができます。
このとき、保存先のポインタ変数をポインタのポインタと言います。
ポインタのポインタを使うとポインタ変数を間接的に参照することが可能になり、複雑なプログラムを作ることもできるようになります。
しかしポインタのポインタは「わかりづらい」として、あまり使わないという人もいます。
ですが知識としてポインタのポインタを知っておくことは、開発者の選択肢を増やします。
この記事ではポインタのポインタについて具体的に↓を見ていきます。
- ポインタのおさらい
- ポインタのポインタ
- ポインタのポインタのポインタ
ポインタのおさらい
簡単にポインタのおさらいをしたいと思います。
たとえばint
型のポインタ変数を宣言するときは↓のようにしました。
int *p; // int型のポインタ変数
このポインタ変数に、int
型変数num
のアドレスを格納するときは↓のようにします。
int num = 10; // int型の整数を保存した変数
int *p = # // numのアドレスをpに保存
↑のようにすると、ポインタ変数p
には変数num
のアドレスが保存され、p
から間接的にnum
を参照することが出来るようになります。
ポインタ変数p
から変数num
の値を書き換えたい時は、
*p = 20; // ポインタを実体化して変数numの値を書き換え
のように変数の頭にアスタリスク(*
)をつけて実体化し、その実体に上書きしたい値を代入します。
↑のようにすると変数num
の値は20
に書き変わります。
このようなポインタを使った参照のことを間接参照と言います。
ポインタを使って間接的に参照するので間接参照と言うことです。
この間接参照は多重化することが可能です。
この多重化した参照の1例が今回のポインタのポインタです。
関連記事: C言語のポインタのメリットとは?コピーしますかメモリを共有しますか
ポインタのポインタ
それではポインタのポインタをやってみましょう。
ポインタのポインタ変数を宣言するにはアスタリスクを2つつけます。
int **pp; // ポインタのポインタ変数
この時、アスタリスク1つを「ポインタ」と読み替えても良いと思います。
アスタリスクが2つ付いてるので「ポインタのポインタ」、つまり3つ付いてれば「ポインタのポインタのポインタ」ということになります。
さらにポインタ変数を1つ用意します。
このポインタ変数p
はポインタのポインタpp
にそのアドレスを保存するポインタです。
int *p; // ポインタ変数
int **pp; // ポインタのポインタ変数
さらにint
型の変数num
を用意します。
int num = 10; // int型の変数num
int *p; // ポインタ変数
int **pp; // ポインタのポインタ変数
ここからポインタのポインタ変数から変数num
の値を参照する方法を解説していきます。
まず変数num
のアドレスをp
に保存します。
int num = 10;
int *p = # // ポインタ変数pに変数numのアドレスを保存
int **pp;
ここまでは普通のポインタです。
さらにポインタ変数p
のアドレスをポインタのポインタ変数pp
に保存します。
int num = 10;
int *p = #
int **pp = &p; // ポインタ変数pのアドレスをppに保存
これでポインタのポインタpp
を使う準備が整いました。
ポインタのポインタから値を参照するには、普通のポインタと同じくアスタリスク(*
)で参照します。
ポインタのポインタではアスタリスク1つで保存されたポインタを参照し、アスタリスク2つで保存されたポインタを実体化します。
#include <stdio.h>
int main(void) {
int num = 10;
int *p = #
int **pp = &p;
printf("p = %p\n", p); // ポインタ変数pに保存されている値を出力
printf("*pp = %p\n", *pp); // ポインタのポインタppを関節参照して値を出力
printf("**pp = %d\n", **pp); // ポインタのポインタppを多重間接参照して値を出力
return 0;
}
↑のコードをコンパイルして実行すると↓のような結果になります。
p = 0x7ffc34b9df24
*pp = 0x7ffc34b9df24
**pp = 10
*「0x7ffc34b9df24
」というアドレス値は環境によって変わります。
ポインタのポインタpp
をアスタリスク1つで参照すると、ポインタ変数p
の値、つまり変数num
のアドレス値を出力しているのがわかります。
そしてpp
をアスタリスク2つで多重間接参照すると変数num
の値を参照できているのがわかります。
ポインタのポインタのポインタ
ポインタのポインタのポインタ、つまり3重の間接参照は基本的にはポインタのポインタの応用です。
原理的にはポインタのポインタと同じです。
ポインタのポインタのポインタは果たして使われるのか? というところですが、私はあまり使ったことがありません。
使うとしてもごくごくたま~にという感じです。
ほとんどの場合、ポインタのポインタで用が足ります。
ポインタのポインタのポインタは↓のように書きます。
#include <stdio.h>
int main(void) {
int num = 10;
int *p = #
int **pp = &p;
int ***ppp = &pp; // ポインタのポインタのポインタにポインタのポインタ変数のアドレスを代入
printf("p = %p\n", p); // ポインタ変数pに保存されている値を出力
printf("*ppp = %p\n", *ppp); // pppを関節参照して値を出力
printf("**ppp = %p\n", **ppp); // pppを2重間接参照して値を出力
printf("***ppp = %d\n", ***ppp); // pppを3重間接参照して値を出力
return 0;
}
↑のコードを実行すると↓のような結果になります。
p = 0x7ffe96da1554
*ppp = 0x7ffe96da1548
**ppp = 0x7ffe96da1554
***ppp = 10
ポインタのポインタのポインタを使っても変数num
の値を最終的に参照できているのがわかります。
このように多重間接参照はアスタリスクの数を増やせば何重にもすることができます。
もちろんコンパイラによってその数には制限があると思いますが。
ただご覧のようにぱっと見であまりわかりやすいコードとは言えません。
ですので、乱用は避けたほうが無難かと思われます。
もっとも、個人開発でチームとして開発することが無い場合は使っても良いかもしれません。
ですがその場合も将来の自分が「なんだこのコードは」となる可能性がありますね。
🦝 < 個人開発あるある
おわりに
今回はポインタのポインタを解説しました。
多重間接参照は知っておくと便利でなので、ぜひこの記事をブックマークしてみてください。
🦝 < 多重請負!
🐭 < やめれ