「ポインタの基礎」

ポインタ

「ポインタ」とは"pointer"と書き「pointする物(者)」 すなわち「指し示す物(者)」という意味である。 では、C言語での「ポインタ」が何を指し示すのかと言うと、 データが格納されている場所である。 C言語に限らずプログラムにおいて、変数に代入されたデータはそれが整数型 であろうが、文字列であろうが、全て記憶領域(メモリ)上に格納されており、 それぞれに専用の場所が割り当てられている。 このデータが格納されているメモリ上の場所をアドレスと呼ぶ。


    int a = 314;
    char s[] = "abc";

上記コード中の整数型変数aや文字列に対して、 例えば、以下の図のような形でメモリ上に配置される。
(実行環境などにより実際のアドレスの値は異なるので注意)
メモリ上のデータの図

C言語ではこのメモリ上に置かれたデータのアドレスをプログラムから扱える ような機能を備えており、これがポインタと呼ぶものである。 そのために、データのアドレスを格納することができる変数を使用することが 出来、このアドレスを格納するための変数をポインタ変数と呼ぶ。

ポインタ変数は以下のように、変数名の直前に"*"をつけて宣言することで 使用可能となる。 それぞれ、変数 pa は整数(int)型のデータが格納されているアドレス を指すポインタ変数であり、変数 ps は文字(char)型のデータが格納さ れているアドレスを指すポインタ変数である。

int  *pa;        /* 整数(int)型ポインタ変数 */
char *ps;        /* 文字(char)型ポインタ変数 */

以下のサンプルコードは 整数型と文字型のそれぞれのポインタ変数を使った例である。
※ サンプルコード中の"&"については次で説明するが、変数のアドレスを 取得する演算子である。 また、printf()関数の書式部での"%p"はポインタ変数を表示するための 書式指定である。

#include <stdio.h>

int
main(void)
{
    int a = 314;
    char s[] = "abc";

    int *pa;         /* 整数(int)型ポインタ変数 */
    char *ps;        /* 文字(char)型ポインタ変数 */

    pa = &a;         /* 変数aのアドレスをポインタ変数paへ格納 */
    ps = &s[1];      /* 文字列の2番目のアドレスをポインタ変数psへ格納 */

    printf("pa=%p\n", pa);
    printf("ps=%p\n", ps);

    return 0;
}

実行結果は以下のようになる。 ポインタ変数のそれぞれの値が表示される。
これらはそれぞれのデータがメモリ上に置かれているアドレスの値である。

pa=0x22f064
ps=0x22f061

ポインタ演算子

ポインタ演算子には、変数が格納されているアドレスを計算する アドレス演算子 &と、 ポインタ変数が指す示すアドレスに格納されている値を参照する 間接演算子 *とがある。

以下のサンプルコードは ポインタ演算子を使った例である。

#include <stdio.h>

int
main(void)
{
    int a = 314;
    int *pa;        /* 整数(int)型のポインタ変数 pa */

    pa = &a;        /* アドレス演算子により変数aのアドレス計算 */

    printf("before write (a=%d)\n", a);

    *pa = 141;      /* 間接演算子によりポインタ経由で変数aの中身を書き換え */

    printf("after write (a=%d)\n", a);

    return 0;
}

実行結果は以下のようになる。 ポインタ変数を経由することで変数の内容を書き換えていることが分かる。

before write (a=314)
after write (a=141)

このコードの場合の変数間の関係は以下の図のようになっている。 (アドレスの値は実行環境により異なるので注意)
変数aとポインタ変数paのどちらもメモリ上に存在し、 ポインタ変数paは変数aのアドレスを値として保持している。 最初、変数aは314という値であるが、ポインタ変数paを経由して その値を141に書き換えられている。

ポインタ変数と変数のメモリ上での関係

下向き矢印

ポインタ変数経由で変数の値を変更

このようにポインタを使うことで変数の値を間接的に読み出したり書き換えたり することが可能である。

複雑なポインタ(ポインタのポインタ)

ポインタ変数も「変数の一種」であるため、メモリ上にデータが格納されている。
そこで、ポインタ変数のアドレスを別のポインタ変数に記憶させるということも 可能である。

以下のサンプルコードは ポインタのポインタを使った例である。

#include <stdio.h>

int
main(void)
{
    int a = 314;
    int *pa;        /* 整数(int)型のポインタ変数 pa */
    int **ppa;      /* 整数(int)型のポインタのポインタ変数 ppa */

    pa = &a;        /* アドレス演算子により変数aのアドレス計算 */
    ppa = &pa;      /* アドレス演算子によりポインタ変数paのアドレス計算 */

    printf("before write (a=%d)\n", a);

    **ppa = 141;    /* 間接演算子を2回使うことでポインタのポインタ経由で変数aの中身を書き換え */

    printf("after write (a=%d)\n", a);

    return 0;
}

実行結果は前述のプログラムと同じであるが、このプログラムでは ポインタのポインタを経由して変数の中身を書き換えている。

このコードの場合の変数間の関係は以下の図のようになっている。 (アドレスの値は実行環境により異なるので注意)
変数a、ポインタ変数paおよび ppaはいずれもメモリ上に存在し、 ポインタ変数paは変数aのアドレスを、 ポインタ変数ppaはポインタ変数paのアドレスを値として保持している。 最初、変数aは314という値であるが、ポインタ変数ppaの指すアドレスに格納 されているアドレスの値を141に書き換えられている。

ポインタのポインタの例(その1)

下向き矢印

ポインタのポインタの例(その2)



文責:大津