Nodachisoft Nodachi Sword Icon
  
@あまじ✎ 2021年2月10日に更新

第2章42 ポインタへのポインタ

イチからゲーム作りで覚えるC言語
第2章41 副作用がある関数、純粋関数、イミュータブル : PREV
NEXT : 第2章43プログラム起動時の引数を読む :

概要

ポインタ変数へのポインタについて、概念を例とあわせて確認していきましょう!

ポインタへのポインタは、ダブルポインタ(二重のポインタ、double pointer)と読ばれることもあります。

ただ、ダブルポインタと発音すると、「double *」と勘違いされることもありますが、これはまったく違うものなのでご注意ください。

その意味の通り、ポインタ変数へのポインタのことで、下のように宣言されます。

int **pointer;

ポインタの識別子である記号「*」が変数の前に2つくっついてます。

ポインタへのポインタ変数と、通常のポインタ変数、通常の変数の関係

ポインタへのポインタ変数、通常のポインタ変数、通常の変数の関係を図で見てみましょう。

 
ex_ref_p.c
#include <stdio.h>
int main(){
  int hp = 100;
  int *p1 = &hp;
  int **p2 = &p1;
  printf("hp を変数から確認: %d。アドレスは %d\n", hp, &hp);
  printf("p1 から hp を確認: %d。アドレスは %d\n", *p1, p1);
  printf("p2 から hp を確認: %d。アドレスは %d\n", **p2, p2);
}

実行結果は下のようになり、3つの方法で変数 100 の中身を確認できました。

result
hp を変数から確認: 100。アドレスは 6684180
p1 から hp を確認: 100。アドレスは 6684180
p2 から hp を確認: 100。アドレスは 6684180

関係は下のようになります。

ポインタへのポインタ

hp の中身が格納されているメモリアドレスへは 「hp」、「*p1」、「**p2」のいずれでもアクセス可能で、同じ結果になります。

ポインタへのポインタアクセス方法

みたいな感じですね。

使いどころ

データ同士をつなぎ合わせる時には、ポインタを使って紐づけることが出来ます。

メモリアドレスを集めて、データ同士の関係を定義することがあります。

例えば、ぷらんくちゃんの持っている袋「bagA」には3つのアイテムが収納できるとします。 この3つの収納場所に、それぞれアイテムデータへのポインタが入っていることを考えてみましょう。

袋の内容

収納場所B,C,Dはそれぞれ、アイテムデータへのポインタを持っています。

格納場所Bは「毒キノコ」へのポインタ、 格納場所C,Dは「穴あきチーズ」へのポインタを格納しています。

このような構造であるとき、袋Aの、格納場所Cには下のような形でアクセスできます。

 
bag_p2p_ex1.c
#include <stdio.h>
int main() {
	char const poison_mushroom[64] = "毒キノコ";
	char const cheese[64] = "穴あきチーズ";

	char *bagA[3];	// バッグに 3 つアイテムが入る
	bagA[0] = &poison_mushroom;	// バッグの1つ目は毒キノコ
	bagA[1] = &cheese;	// バッグの2つ目は穴あきチーズ
	bagA[2] = &cheese;	// バッグの3つ目は穴あきチーズ

	char** planc_equip[5]; // 5 つ装備できる
	planc_equip[0] = &bagA; // 袋を装備させる

	printf("ぷらんくちゃんが装備している袋Aの 2 番目には %s が入っています。\n", *((*planc_equip)+1) );
}

実行すると下のような結果となります。

実行結果
プレイヤーが装備している袋の 2 番目には 穴あきチーズ が入っています。

まず、袋A には、3つのアイテムへのポインタがあり、 それぞれアイテムデータ(今回はアイテムの名前を表す文字列データ)へのポインタを代入しています。

bag_p2p_ex1.c
char *bagA[3];	// バッグに 3 つアイテムが入る
bagA[0] = &poison_mushroom;	// バッグの1つ目は毒キノコ
bagA[1] = &cheese;	// バッグの2つ目は穴あきチーズ
bagA[2] = &cheese;	// バッグの3つ目は穴あきチーズ

また、ぷらんくちゃんの装備を表す planc_equip は、5つの装備をすることができます。今回は袋Aのみ装備させる設定とします。

bag_p2p_ex1.c
char** planc_equip[5]; // 5 つ装備できる
planc_equip[0] = &bagA; // 袋を装備させる

plang_equip[0] には袋Aを装備させたいので、袋Aへのポインタアドレスを代入しています。

このとき、planc_equip 変数はポインタへのポインタとして定義することになります。

bag_p2p_ex1.c
printf("ぷらんくちゃんが装備している袋Aの 2 番目には %s が入っています。\n", *((*planc_equip)+1) );

最後に、袋A の添え字[1] (つまり袋Aの中の 2 番目のアイテム)に格納されているアイテムデータにアクセスします。

*plancequip は、 plancequip に入っているポインタアドレスの先データを表しますので、これは bagA の先頭アドレス(つまり bagA[0] のアドレス)を示しています。

*planc_equip は bagA、もしくは &bagA[0] と置き換えられます。(実際、このように書き換えても同じ結果が得られます)

*(bagA + 1) は配列 bagA を 1 つの(char *)ぶんずらしたポインタアドレスへのアクセスを意味します。つまり、bagA[1] へアクセスしたのと同じ結果が得られます。

このようにしてポインタへのポインタを使うことで、 ぷらんくの装備[0] のポインタから、袋A にアクセスし、 袋A[1] のポインタからアイテム「穴あきチーズ」にアクセスすることができました。

補足

C言語に近い、プログラミング言語の C++ では、ポインタへのポインタを基本的に使う必要がないよう、標準ライブラリも工夫されています。 ポインタへのポインタは C++ でも使うことはできますが、そもそも読みづらいしバグの原因に繋がりやすいのでなるべく避けた方が良いでしょう。

参考

(英語)N1570 - 6.3.2.3 Pointers

イチからゲーム作りで覚えるC言語
第2章41 副作用がある関数、純粋関数、イミュータブル : PREV
NEXT : 第2章43プログラム起動時の引数を読む :
 
 
送信しました!

コメント、ありがとうございます。

なんかエラーでした

ごめんなさい。エラーでうまく送信できませんでした。ご迷惑をおかけします。しばらくおいてから再度送信を試していただくか、以下から DM などでご連絡頂ければと思います。

Twitter:@NodachiSoft_jp
お名前:
 
連絡先:
 
メッセージ:
 
戻る
内容の確認!

以下の内容でコメントを送信します。よろしければ、「送信」を押してください。修正する場合は「戻る」を押してください

お名前:
 
連絡先:
 
メッセージ:
 
Roboto からの操作ではないという確認のため確認キーを入れてください。
確認キー=95
戻る
 / 
送信確認へ
コメント欄
コメント送信確認へ

関連ありそうな記事(5件)です!

第4章3 C言語コンソール上で迷路脱出プログラム・迷路の自動生成

#C11仕様#C言語#ゲームプログラミング✎ 2021-05-15
C言語のコンソールゲームで迷路脱出ゲームプログラムの作り方を確認します。迷路は自動生成されます
広告領域
追従 広告領域
目次
第2章42 ポインタへのポインタ
第2章42 ポインタへのポインタ
概要
概要
ポインタへのポインタ変数と、通常のポインタ変数、通常の変数の関係
ポインタへのポインタ変数と、通常のポインタ変数、通常の変数の関係
使いどころ
使いどころ
補足
補足
参考
参考
Nodachisoft © 2021