【C言語】ポインタのポインタ(ダブルポインタ)を解説【図解】

ポインタのポインタ解説ページアイキャッチ

このページにはプロモーションが含まれています

このページではポインタのポインタ(ダブルポインタ)について説明したいと思います。

ポインタについては下記の記事で解説していますので、まだ理解が不十分という方は是非読んでみてください。

ポインタの解説ページアイキャッチ 【C言語】ポインタを初心者向けに分かりやすく解説

ポインタのポインタ…

ダブルポインタ…

ポインタを理解するのにも苦労したのに理解できるか不安…

うーん、そう感じるかもしれないけど、

ポインタがしっかり理解できていればポインタのポインタは実は簡単だよ

ポインタのポインタと聞いて身構える必要はありません!

ポインタについてしっかり理解していれば、おそらくすんなり理解できると思います。

さらに、”ポインタのポインタ” を理解することでポインタの理解も深まります!

では早速、ポインタのポインタについて解説していきたいと思います。

ポインタ(おさらい)

前述の通り、”ポインタのポインタ” を理解するためには、まずはポインタについて理解しておいた方が良いです。

ということで、”ポインタのポインタ” について解説する前に、おさらいとして、ポインタとはどのようなものであるかについて簡単に説明しておきたいと思います。

ポインタの理解はバッチリという方は、この章はスキップして次のポインタのポインタまで進んでいただければと思います。

ポインタとは

ポインタは他の変数を指す(他の変数のアドレスを格納する)ことができる変数ですね!

ポインタが他のデータを指す様子

このようにポインタが変数を指すことができるのは、変数が「メモリ上に配置」され、その変数に対して「アドレスが割り振られる」からです。

ポインタにアドレスが格納される様子

実際には、ポインタは変数でなくてもメモリ上に配置されているものなら指すことができます(関数など)。

MEMO

このページでは「どの変数がどの変数を指しているか」が分かりやすくなるように上図のようなメモリ空間の図を使用して説明しています

サイズやアドレス値も正確に書くと図や説明が難解になるため、このページではこれらがてきとうなものになっているので注意してください

このページでは「どの変数がどの変数を指しているか」のみに注目し、まずはポインタのポインタについてのイメージを掴んでいただければと思います

スポンサーリンク

ポインタの使い方

続いてポインタの使い方についておさらいしておきましょう!

ポインタの変数宣言

ポインタを使用するためには、intchar などの基本的な型の後ろに * を付加して変数宣言を行います。

この変数宣言時の * がポインタであることを示す目印になっているわけですね!

ポインタの変数宣言
/* ポインタの変数宣言 */
int *p;

ポインタへのアドレス格納

また、変数が配置されているメモリ上のアドレスは、「変数名の前に & を付加する」ことで取得することができます。

そして、ポインタでは、この変数名に & を付加して取得したアドレスを格納することができます。

アドレスの格納
int *p;
int a;

/* アドレスの格納 */
p = &a;

これでこのポインタは変数 a を指すことになります。

ポインタpが変数aを指す様子

ポインタからのデータアクセス

さらに、「ポインタの変数名の前に * を付加する」ことで、そのポインタが指しているアドレスのデータにアクセスすることができます。

アドレスのデータへのアクセス
int *p;
int a;

p = &a;

/* アドレスのデータへのアクセス */
*p = 100;

アクセスしたアドレスにはデータを格納したり、そのアドレスからデータを取得したりすることができます。

ポインタからデータにアクセスする様子

ポインタへの加減算

ポインタには加減算を行うことで格納されているアドレスを増減させることが可能です。

で、この時に実際にアドレスが増減する値は型によって異なります。

具体的には、ポインタ変数に対して +1 した際には、アドレスは「そのポインタ変数の型の基になる型のサイズ」分増加することになります(-1 の場合はそのサイズ分減少する)。

要は、ポインタの型から * を削除した型のサイズ増加します。

例えば char* 型なら char 型のサイズ分(つまり 1 バイト)、short* 型なら short 型のサイズ分(つまり 2 バイト)増加することになります。

詳しくは下記ページで解説していますので詳しく知りたい方はこちらをご覧ください。

ポインタの型の解説ページアイキャッチ 【C言語】ポインタの「型」について解説

ここまでは理解しているつもり!

でもポインタのポインタってまだイメージ湧かないなぁ

ポインタのポインタも結局は「ポインタの一種」なんだ

なのでここまで理解していればポインタのポインタについてもバッチリ理解できると思うよ

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

では本題である「ポインタのポインタ」について解説していきます(ポインタのポインタは「ダブルポインタ」とも呼ばれます)。

ポインタのポインタとは

まず、「ポインタのポインタ」はポインタとほぼ変わりません

前述の通り、ポインタは他の変数を指す(他の変数のアドレスを格納する)ことができる変数です。

これは “ポインタのポインタ” も一緒です。”ポインタのポインタ” は他の変数を指す(他の変数のアドレスを格納する)ことができる変数です。

ただし “ポインタのポインタ “場合、この「他の変数」がポインタになります。

つまり、“ポインタのポインタ” とは「ポインタを指すポインタ」のことをいいます。まさに「ポインタ」のポインタです!

ポインタのポインタがポインタを指す様子

またポインタ(おさらい)でポインタが他の変数を指すことができる理由を下記のように説明しました。

このようにポインタが変数を指すことができるのは、変数が「メモリ上に配置」され、その変数に対して「アドレスが割り振られる」からです。

で、ポインタも結局は変数です。変数なので「メモリ上に配置」され、「アドレスが割り振られる」ことになります。

ポインタのポインタにアドレスが格納される様子

なので、ポインタもアドレスを持ち、”ポインタのポインタ” はこのポインタを指すことができます。

スポンサーリンク

ポインタのポインタの使い方

続いて “ポインタのポインタ” の使い方について解説していきます!

基本的な考え方はポインタと同じです!

ポインタのポインタの変数宣言

“ポインタのポインタ” を使用するためには、intchar などの基本的な型の後ろに ** を付加して変数宣言を行います。

“ポインタのポインタ” においては *2つ付けるところがポイントです。

この変数宣言時の ** が “ポインタのポインタ” であることを示す目印になっているわけですね!

ポインタのポインタの変数宣言
/* ポインタのポインタの変数宣言 */
int **pp;

ポインタのポインタへのアドレス格納

また、ポインタが配置されているメモリ上のアドレスは、他の変数同様に「変数名の前に & を付加する」ことで取得することができます。

そして、“ポインタのポインタ” では、このポインタの変数名に & を付加して取得したアドレスを格納することができます。

アドレスの格納
int **pp;
int *p;

/* アドレスの格納 */
pp = &p;

これで、この “ポインタのポインタ” pp はポインタ p指すことになります。

ポインタのポインタppがポインタpを指す様子

ポインタのポインタからのデータアクセス

さらに、”ポインタのポインタ” においても「”ポインタのポインタ” の変数名の前に * を付加する」ことで、その “ポインタのポインタ” が指しているアドレスのデータにアクセスすることができます。

アドレスのデータへのアクセス
int **p;
int *p;
int a;

pp = &p;
/* アドレスのデータへのアクセス */
*pp = &a;

ポインタのポインタへの加減算

これも考え方はポインタと同じです。

“ポインタのポインタ” 変数に対して +1 した際には、アドレスは「その “ポインタのポインタ” 変数の型の基になる型のサイズ」分増加することになります(-1 の場合はそのサイズ分減少する)。

基になる型とは、”ポインタのポインタ” の型から * を1つ削除した型のサイズです。

つまりポインタ変数の型のサイズ分増加することになります。

ポインタ変数の型のサイズは全て同じです(環境によって異なりますが 4 バイト or 8 バイトだと思います)ので、型によって増減値が変わらないところはポインタと異なりますが、アドレスの増減値に関しても考え方はポインタと一緒です。

なるほど!

“ポインタのポインタ” も結局ポインタと考えるとすんなり理解できるね!

確かにポインタも考えてみれば単なる変数だもんね!

そうそう!まずは分かりやすいイメージをもって苦手意識をなくすことが大事だよ!

ここからは “ポインタのポインタ” とポインタの違いと

ポインタのポインタの注意点について解説していくよ

“ポインタのポインタ” とポインタの違い

ここまでは “ポインタのポインタ” とポインタを同様のものとして考えて解説してきました。

ここからはこの2つの違いを踏まえて “ポインタのポインタ” について解説していきたいと思います。

“ポインタのポインタ” とポインタの決定的な違いは下記になります。

  • ポインタのポインタ:指す対象がポインタ(つまりアドレスが格納された変数)
  • ポインタ:指す対象がデータの実体(値そのものが格納された変数)

ポインタのポインタでアクセスできるデータはポインタ

つまり、”ポインタのポインタ” が指す先にはアドレスが格納されています。ですので “ポインタのポインタ” の変数の前に * を付加して取得できるデータはアドレスを格納する変数、つまりポインタとして考えられます。

*ppがポインタである様子

したがって、”ポインタのポインタ” の変数名の前に * を付加すると、ポインタ同様にして扱うことができます。

そして、ポインタでも同様に、変数名の前に * を付加することで、このポインタが指すアドレスのデータにアクセスすることができます。

ポインタのポインタが指すポインタの指すデータにアクセスする様子1

ポインタのポインタには ** を付けてデータアクセス可能

こういった性質があるため、”ポインタのポインタ” の変数名の前に *2つ付加することで、すなわち ** を付加することで、「”ポインタのポインタ” が指すポインタ」が指すアドレスのデータにアクセスすることができます。

ポインタのポインタが指すポインタの指すデータにアクセスする様子2

ただし、こういった使い方ができるのは「”ポインタのポインタ” がポインタを指し、その指されたポインタがデータそのもの(ポインタでない変数など)を指しているときだけ」です。

ポインタでも、他の変数などのデータそのものを指していない状態(アドレスが格納されていない状態)で * をポインタの変数名に付加してデータにアクセスしても、どのアドレスにアクセスするか分かりません。

多くの場合、メモリアクセス違反が発生します。

ポインタ使用時にメモリアクセス違反が発生する様子

これと一緒で、”ポインタのポインタ” においても、ポインタをしっかり指させた状態で * を使ってデータにアクセスする必要があります。

ポインタのポインタ使用時にメモリアクセス違反が発生する様子1

さらに “ポインタのポインタ” の場合、その “ポインタのポインタ” が指しているポインタにおいても、他のデータをしっかり指させた状態で * を使ってデータにアクセスする必要があります。

ポインタのポインタ使用時にメモリアクセス違反が発生する様子2

例えば下記はダメな “ポインタのポインタ” の使い方です。ポインタ p にアドレスが格納されていない状態でポインタ p の指すアドレスのデータにアクセスしているのでメモリアクセス違反になります。

間違ったデータへのアクセス
#include <stdio.h>

int main(void) {
    int **pp;
    int *p;

    /* ppにpを指させる */
    pp = &p;

    /* pの指すアドレスの値を表示 */
    printf("%d\n", **pp);

    return 0;

}

要は、”ポインタのポインタ” においては、その “ポインタのポインタ” だけでなく、そのポインタのポインタの先のポインタがどこを指しているかもしっかり考慮してプログラミングする必要があるということです。

ポインタのポインタとポインタがデータまでつながっている様子

先ほどの例を正しく動作させるためには、下記のようにポインタ p にアドレスを格納してからポインタ p の指すアドレスのデータにアクセスしてやれば良いです。

正しいデータへのアクセス
#include <stdio.h>

int main(void) {
    int **pp;
    int *p;
    int a = 100;

    /* ppにpを指させる */
    pp = &p;

    /* pにaを指させる */
    p = &a; /* *pp = &a でも良い */

    /* pの指すアドレスの値を表示 */
    printf("%d\n", **pp);

    return 0;

}

考慮する必要があることが多くなるので、この点だけはポインタに比べるとちょっと難しいかなぁと思います。

うーん、

こう聞くとやっぱり難しそう…

結局注意点の本質はポインタと同じだよ

要は、しっかりポインタの指す先を考慮してプログラミングしましょうって話だね

“ポインタのポインタ”の場合は “ポインタのポインタ” とポインタの両方に対してしっかり考慮が必要ってだけだよ

そう聞くとなんか簡単そう!
(単純だなぁ…)

ポインタのポインタを用いたプログラム

“ポインタのポインタ” について理解を深めるために、実際のプログラムを動作させて “ポインタのポインタ” の動きを確認していきましょう!

スポンサーリンク

ソースコード

今回は下記のソースコードのプログラムで “ポインタのポインタ” の動作を解説していきたいと思います。

ポインタのポインタを用いたプログラム
#include <stdio.h>

int main(void) {

    int **dptr;
    int *ptr;
    int data;

    data = 123;

    ptr = &data;
    dptr = &ptr;


    printf("&data  = %p\n", &data);

    printf("&ptr   = %p\n", &ptr);
    printf("ptr    = %p\n", ptr);
    printf("*ptr   = %d\n", *ptr);

    printf("dptr   = %p\n", dptr);
    printf("*dptr  = %p\n", *dptr);
    printf("**dptr = %d\n", **dptr);

    return 0;
}

実行すると下記のような結果になりました(環境や実行したタイミングによって値は異なります)。

&data  = 0x7ffeefbff464
&ptr   = 0x7ffeefbff468
ptr    = 0x7ffeefbff464
*ptr   = 123
dptr   = 0x7ffeefbff468
*dptr  = 0x7ffeefbff464
**dptr = 123

0x で始まるのがアドレスです。

なんだかよく分からないアドレスの値になっていますが、アドレスの値自体は気にしなくて良いです。

注目していただきたいのは、どれとどれが同じ値になっているかです。

まず &dataptr が同じ値になっていますね。これはつまりポインタ ptr に data のアドレスが格納されているということになります(ptrdata を指している)。

さらに &ptrdptr が同じ値です。これはつまりポインタのポインタ dptr に ptr のアドレスが格納されているということになります(dptrptr を指している)。

したがって data と ptr と dptr の関係を図示すると下のようになります。

dptrとptrとdataの関係図

プログラムの動き

なぜ上図のような関係になるのかの詳細を、プログラムの動きを踏まえて考えていきましょう!

まず下記の変数宣言により、3つの変数 dptrptrdata がメモリ上に配置されます。

変数宣言
int **dptr;
int *ptr;
int data;

図で表すと下のようになります。

変数がメモリ上に配置される様子

次に下記により、data に値が格納されます。

変数への値の格納
data = 123;

図で表すと下のようになります。

dataに数値が格納される様子

続いて、下記によって ptrdata 変数のアドレスが格納されます。つまり、ptrdata を指すことになります。

ポインタへのアドレス格納
ptr = &data;

図で表すと下のようになります。ここは単純にポインタの話ですね!

ptrがdataを指す様子

ですので、ptr の値を表示すると、data のアドレス(つまり &data)が表示されることになります。

&data = 0x7ffeefbff464
ptr = 0x7ffeefbff464

したがって、*ptr の値を表示すると、ptr が指す data の値である 123 が表示されます。

*ptr = 123

ポインタからのデータへのアクセスを図で考えると下図のようになります。

ptrの指すアドレスのデータにアクセスする様子

続いて dptr を見ていきましょう。ptrがポインタであるのに対し、dptr は “ポインタのポインタ” であり、ポインタを指すポインタです。

この dptr には下記によって ptr のアドレスが格納されます。つまり dptrptr を指すことになります。

ポインタのポインタへのアドレス格納
dptr = &ptr;

これによりポインタ変数のアドレスを他のポインタが指すことになります。このポインタ変数を指すポインタが、”ポインタのポインタ” です。

図で表すと下のようになります。

dptrがptrを指す様子

dptrptr を指していますので、dptr と &ptr は同じアドレス値になります。

&ptr = 0x7ffeefbff468
dptr = 0x7ffeefbff468

また dptrptr を指していますので、*dptr の値はポインタ ptr に格納されているアドレスの値と同じになります。さらに前述の通り、ptrdata を指していますので、*dptrptr&data を表示すると同じアドレスの値が表示されることになります。

&data = 0x7ffeefbff464
ptr = 0x7ffeefbff464
*dptr = 0x7ffeefbff464

つまり、*dptr で取得できるデータは変数 data のアドレス、つまり変数 data を指しているポインタです。

なので、このポインタに対して * を付加すれば、変数 data に格納されている値を取得できることになります。

したがって、*dptr の前に * を付加した **dptr では、変数 data の値を取得できることになります。

**dptr = 123

この辺りの動きを図示すると下図のようになります。

dptrからptrの指すアドレスのデータにアクセスする様子

なるほど!

“ポインタのポインタ” が分かった気がするよ

難しそうだったけど、図があると分かりやすいね!

そう!

ポインタは絶対図を書いた方が動きを理解しやすいと思うよ!

特にプログラムがうまく動作しないような時は、図を書いてポインタの動きを整理してみることをオススメするよ

ポインタのポインタの使いどころ

では “ポインタのポインタ” はどういった場面で使われるのでしょうか?

この点について解説していきたいと思います。

スポンサーリンク

関数の引数

“ポインタのポインタ” が一番利用されるのは「関数の引数」です。

特に「関数内でポインタに格納されているアドレスを変更したい場合」に使用されます。

下記ページでも解説していますが、関数内で引数の変数の値を変更しても、関数呼び出し元で使用される変数にはその変更は反映されません。

ポインタの解説ページアイキャッチ 【C言語】ポインタを初心者向けに分かりやすく解説

これは、関数呼び出し時に指定する変数と、関数の引数で渡される変数は全く異なるデータだからです(値がコピーされているだけ)。

関数内で変数等の値を変更するためには、引数としてはその変数を指すポインタを渡す必要があります。

ポインタ変数は関数呼び出し元と関数内とで異なりますが、それらには同じアドレスが格納されています。つまり、関数内のポインタから関数呼び出し元の変数を指すことができます。

関数内から関数呼び出し元の変数を指す様子

ですので、そのアドレスにあるデータを変更すれば、関数呼び出し元の変数を変更することになります。

つまり、引数にポインタ(アドレス)を渡すことで、関数内から関数呼び出し元の変数やデータを変更するようなことができます(この辺りは上記のページで解説しています)。

関数内から関数呼び出し元の変数を変更する様子

これと一緒で、ポインタに格納されているアドレス(つまりポインタの指す先)を関数内で変更したい場合は、そのポインタを指すポインタ、つまりは “ポインタのポインタ” を関数の引数に指定する必要があります。

例えば下記のソースコードの getPtr 関数では、引数 ptrdata のアドレスを格納していますが、関数の引数の ptr と、関数呼び出し元の ptr は全く異なるデータなので、関数呼び出し元の ptr には data は格納されていません。

なので、最後の printf で表示される値は 1000 にならないはずです(たまたまなる場合もある)。

ポインタに値が格納されない例
#include <stdio.h>

static int data = 1000;

void getPtr(int *ptr) {
    ptr = &data;
}

int main(void){

    int *ptr;

    getPtr(ptr);

    printf("%d\n", *ptr);

    return 0;
}

一方で、下記のソースコードの getPtr 関数では、引数 dptr は “ポインタのポインタ” なので、*dptrdata のアドレスを格納してやれば、関数呼び出し元の ptrdata のアドレス格納されることになります。

ポインタに値が格納される例
#include <stdio.h>

static int data = 1000;

void getPtr(int **dptr) {
    *dptr = &data;
}

int main(void){

    int *ptr;

    getPtr(&ptr);

    printf("%d\n", *ptr);

    return 0;
}

こんな感じで「関数内でポインタに格納されているアドレスを変更したい場合」には “ポインタのポインタ” を利用することがあります。

2次元データ

後は2次元のデータを利用するような際にも “ポインタのポインタ” を使用することがあります。

“ポインタのポインタ” で2次元のデータを扱う例は下記ページで紹介していますので、詳しく知りたい方は是非読んでみてください。

二次元データの扱い方を解説するページのアイキャッチ C言語で2次元データをいろいろな方法で扱ってみる(二次元配列・ポインタのポインタなど)

例えば画像などは縦方向と横方向に画素が広がる2次元のデータとして考えて扱われることがあります。

ポインタの学習

次は実践的な話ではないですが、”ポインタのポインタ” の利用によりポインタへの理解をより深められると思います。

ポインタは「どこを指しているか」をしっかり考えて利用することが重要です。

さらに “ポインタのポインタ” は、この “ポインタのポインタ” 自身だけでなく、これが指しているポインタも含めて「どこを指しているか」をしっかり考える必要があります。

逆にいうと、”ポインタのポインタ” は「どこを指しているか」をしっかり考えてプログラミングしないと上手く動作してくれないので、その修正やデバッグなどを繰り返すことで自然と「どこを指しているか」を考える力がつくと言えると思います。

確かに!

“ポインタのポインタ” を学ぶことで、よりポインタがデータを指すイメージが身についたよ!

前にも言ったけど、特に最初は図を書きながら、どのポインタがどこを指しているかを整理しながらプログラミングするのがオススメ!

スポンサーリンク

ポインタのポインタのポインタ(トリプルポインタ)

次は、ここまでの話を踏まえてさらに応用的なポインタの使い方について紹介したいと思います。

“ポインタのポインタ” がポインタを指すことができるのは、ポインタがメモリ上に配置されてるからだよね?

“ポインタのポインタ” もメモリ上に配置されてるんだから、これも他のポインタから指すことができるの?

いい気付きだね!できるよ!

“ポインタのポインタ” を指すポインタだから “ポインタのポインタのポインタ” って言っていいかもね

下の図はポインタのポインタを用いたプログラムでお見せした “ポインタのポインタ” である dptr、ポインタである ptrint 型の変数である data の3つの関係性を示した図になります。

dptrとptrとdataの関係図

ポインタの ptr が “ポインタのポインタ” の dptr で指すことができるように、”ポインタのポインタ” の dptr もメモリ上に存在してアドレスが割り振られているため、他のポインタにより指すことができます。

ポインタのポインタをトリプルポインタが指す様子

このような、”ポインタのポインタ” を指すポインタをトリプルポインタと呼びます。

詳細な説明は省略しますが、”ポインタのポインタ” と同様の考え方で使用することができます。

下記はトリプルポインタを用いたプログラムのソースコード例になります。

トリプルポインタ
#include <stdio.h>

int main(void){

    int ***tptr;
    int **dptr;
    int *ptr;
    int data;

    data = 123;

    ptr = &data;
    dptr = &ptr;
    tptr = &dptr;

    printf("&data   = %p\n", &data);
    printf("data    = %d\n", data);
    
    printf("&ptr    = %p\n", &ptr);
    printf("ptr     = %p\n", ptr);
    printf("*ptr    = %d\n", *ptr);

    printf("&dptr   = %p\n", &dptr);
    printf("dptr    = %p\n", dptr);
    printf("*dptr   = %p\n", *dptr);
    printf("**dptr  = %d\n", **dptr);

    printf("tptr    = %p\n", tptr);
    printf("*tptr   = %p\n", *tptr);
    printf("**tptr  = %p\n", **tptr);
    printf("***tptr = %d\n", ***tptr);

    return 0;
}

実行結果は下記の通りになります。どのポインタがどのデータを指しているかを図を書いて考えてみると、より理解が深まると思います。

&data   = 0x7ffeefbff45c
data    = 123
&ptr    = 0x7ffeefbff460
ptr     = 0x7ffeefbff45c
*ptr    = 123
&dptr   = 0x7ffeefbff468
dptr    = 0x7ffeefbff460
*dptr   = 0x7ffeefbff45c
**dptr  = 123
tptr    = 0x7ffeefbff468
*tptr   = 0x7ffeefbff460
**tptr  = 0x7ffeefbff45c
***tptr = 123

“ポインタのポインタのポインタ” もメモリ上に配置されるんだよね?

もしかして “ポインタのポインタのポインタのポインタ” もあるの?

一応あるよ!

でも実際に使われてる例は見たことないけどね…

ここまで解説してきた内容を踏まえれば、実は下記のようにもっと深い階層を指すポインタも利用することはできます。

が、こんな使い方をしているプログラムは見たことないですね…。

一応こういった使い方ができることや、なぜこのような使い方ができるのかは覚えておくと良いと思います!

ポインタのポインタの応用
#include <stdio.h>

int main(void){

    int *******ptr7;
    int ******ptr6;
    int *****ptr5;
    int ****ptr4;
    int ***ptr3;
    int **ptr2;
    int *ptr1;
    int a = 100;

    ptr1 = &a;
    ptr2 = &ptr1;
    ptr3 = &ptr2;
    ptr4 = &ptr3;
    ptr5 = &ptr4;
    ptr6 = &ptr5;
    ptr7 = &ptr6;

    printf("%d\n", *******ptr7);

    return 0;
}

まとめ

このページでは “ポインタのポインタ” について解説しました。

“ポインタのポインタ” も結局はポインタの一種です。

ポインタが使いこなせれば “ポインタのポインタ” も使いこなすことができると思います。

ただし、”ポインタのポインタ” が指すデータはポインタになります。ここだけ注意です。

指すデータがポインタなので、”ポインタのポインタ” の変数名に ** を付加することで、「”ポインタのポインタ” が指すポインタ」が指すアドレスのデータにアクセスするような使い方もできます。

ポインタ同様にどのデータを指しているかをしっかり考えて利用するようにしましょう!

オススメの参考書(PR)

C言語一通り勉強したけど「ポインタがよく分からない」「ポインタの理解があやふや」「もっとC言語の理解を深めたい」という方には、下記の「C言語ポインタ完全制覇」がオススメです!

この本の主な内容は下記の通りで、通常の参考書では50ページくらいで解説するポインタを、この本では約 "360ページ" 使って幅広く・深く解説しています。

  • C言語でのメモリの使い方
  • 配列とポインタの関係性
  • ポインタのよくある使い方
  • ポインタの効果的な使い方

一通りC言語を学んだだけだと "理解があやふやになってしまいがち" "疑問に思いがち" な内容に対する明確な解説が多いため、特にポインタやC言語の理解があやふやという方にはオススメの本です。

また、C言語においてポインタはまさに "肝" となる機能ですので、ポインタについてより深く学ぶことでC言語全体の理解を深めることにもつながります。

ポインタ・C言語についてより深く理解するための本としては現状1番のオススメの本です。

ただし、他の入門書等で "一通りC言語を学んでいる" 方向けの解説になっているので、"C言語を始めるにあたっての最初の入門書" として利用すると難易度が高いので注意してください。

入門用のオススメ参考書は下記ページで紹介していますので、こちらも是非参考にしていただければと思います。

https://daeudaeu.com/c_reference_book/

同じカテゴリのページ一覧を表示

1 COMMENT

safaf

分かりにくいのは表現に不備があるから
ポインタのポインタではなく

正確には、ポインタが格納された変数のポインタ

現在コメントは受け付けておりません。