C言語のポインタとは

目次

ポインタとは

ポインタは、変数のアドレス(住所)を格納する変数です。

ポインタを使うことで、間接的に他の変数の値を参照・変更したり、配列や関数を効率的に扱うことができます。

以下は、ポインタを使用したコードです。

#include <stdio.h>

int main(void)
{

  // 1 変数への代入
  int a = 10;
  printf("%d\n", a); // 10

  // 2 ポインタ変数
  int *p;                    // ポインタ変数pを宣言
  p = &a;                    // pにaのアドレスを代入
  printf("%p\n", (void *)p); // 0061FF18 アドレス

  // 3 間接参照(*演算子)
  printf("%d\n", *p); // 10 間接参照 *演算子

  // 4 値の変更も可能
  *p = 20;                 // 20に変更
  printf("*p = %d\n", *p); // *p = 20
  printf("a = %d\n", a);   // a = 20

  return 0;
}

上記コードの説明

  // 1 変数への代入
  int a = 10;
  printf("%d\n", a); // 10

変数aに値を代入しています。&aとした場合は、変数aのアドレスになります。

 

  // 2 ポインタ変数
  int *p;                    // ポインタ変数pを宣言
  p = &a;                    // pにaのアドレスを代入
  printf("%p\n", (void *)p); // 0061FF18 アドレス

pはint型のオブジェクトを指すことができるポインタ変数です。宣言の*は変数名の一部ではなく、pの型がintへのポインタであることを表します。
宣言のint *p;に出てくる*と、後で使う*pの*は別の意味です。前者は型の一部、後者は指す先をたどる演算子です。
なおint* p;と書いても文法上は同じですが、int* p,q;とするとqはポインタにならないため(*が型側に付いているように見えてしまい、int* p, q;としたときに両方ポインタだと錯覚しやすくなるため)、int *p;の書き方が推奨されます。

両方ポインタの例:

int *p, *q;

注意点

int *p;と宣言しただけのポインタには、意味のない不定値が入っています。この状態で*pを使うと、プログラムがどこを書き換えるか誰にも分かりません。運が良ければ即座に落ちますが、運が悪いと壊れたまま動き続けます。

指す先がすぐ決まるなら、宣言と同時に代入します。

int a = 10;
int *p = &a; // 宣言と同時に指す先を決める

すぐ決まらないならNULLを入れておきます。

int *p = NULL; // 「まだ何も指していない」ことを明示する

if (p != NULL) { // 使う前に必ず確認する
*p = 20;
}

NULLはどのオブジェクトも指さないことを表す特別な値で、<stdio.h> などをインクルードすれば使えます。p = NULL; としておけば、少なくとも未設定であることをプログラム自身が判定できます。不定値のままでは、この判定すらできません。

 

  // 3 間接参照(*演算子)
  printf("%d\n", *p); // 10 間接参照 *演算子

*pは、pが指す先にある値、つまりaの値を取り出すことを意味します。

 

  // 4 値の変更も可能
  *p = 20;                 // aの値を20に変更
  printf("*p = %d\n", *p); // *p = 20
  printf("a = %d\n", a);   // a = 20

pが指している先、つまり変数aに20を代入しています。p自身の値(aのアドレス)は変わりません。

※13行目の%pの出力形式は処理系依存で、0061FF18のような値は環境ごとに変わります(実行のたびに変わることも普通です)。

配列の場合

#include <stdio.h>

int main(void)
{
  int a[3] = {10, 20, 30};

  int *p = a;

  printf("%d\n", a[0]);            // 10
  printf("%d\n", a[1]);            // 20
  printf("%d\n", a[2]);            // 30
  printf("%d\n", *p);              // 10
  printf("%d\n", *(p + 1));        // 20
  printf("%d\n", *(p + 2));        // 30
  printf("%p\n", (void *)p);       // 0061FF10 アドレス
  printf("%p\n", (void *)(p + 1)); // 0061FF14 アドレス
  printf("%p\n", (void *)(p + 2)); // 0061FF18 アドレス

  return 0;
}

int *p = a;は、

int型の値を指すポインタ変数pを作り、配列aの先頭要素のアドレスを代入しています。

配列名aは、多くの場面で配列の先頭要素 a[0] のアドレスとして扱われます。

 

ポインタ変数への代入で普通の変数では、以下のように&aが必要ですが、

int a = 10;
int *p;
p = &a;

配列では以下のように&は不要です。
int a[3] = {10, 20, 30};
int *p = a;

※15-17行目の%pの出力形式は処理系依存で、0061FF18のような値は環境ごとに変わります(実行のたびに変わることも普通です)。

関数の場合

#include <stdio.h>

void change(int *p)
{
  *p = 20;
}

int main(void)
{
  int a = 10;
  printf("%d\n", a); // 10
  change(&a);
  printf("%d\n", a); // 20
  return 0;
}

12行目は、関数changeに、変数aのアドレスを渡しています。
3行目は、int型を指すポインタを引数として受け取る関数です。
5行目は、そのアドレスにある値を書き換えています。

関連の記事

C++ if文 条件分岐を行うサンプル

△上に戻る