LoginSignup
63
37

More than 3 years have passed since last update.

C言語のポインタを日本一わかりやすく解説する〜導入編〜

Posted at

はじめに

 C言語は、私がエンジニアになるにあたって一番最初に勉強したさせられたプログラミング言語です。「プログラミングって楽しいかもしれない...!」と思い始めた矢先に登場するこのポインタと呼ばれる単元は、当時のプログラミング初心者であった私を挫折させました。正直に言ってしまえばくっそ難しいです。
 それでも乗り越えたい...!という初心者エンジニアさんにこの記事をお送りしたいと思います。

ポインタとは?

 ポインタ(pointer)と聞くと、プレゼンテーション等でよく使われるこれを思い浮かべませんか?
images.jpeg
 手に持ってスライド中の大切なところを指し示すために使われるイメージの強いポインタですが、この「指し示す」というのが非常に重要です。C言語においてもレーザーポインタのように指し示すことができます。

ポインタが指し示すもの

 プレゼン中でスライドの今話している該当箇所をレーザーポインタで指し示すのと同じように、ポインタはとある場所の1点を指し示すことができます。
 C言語におけるポインタはなんの場所の1点を指し示すかというと、アドレス(address)を指し示します。そしてなんのアドレスかというと、オブジェクト(object)のアドレスです。
 さらに噛み砕いていきます。C言語では変数と呼ばれるものを宣言することができました。例えば、次のようなプログラムにおいて

variable.c
#include<stdio.h>

int main(void)
{
    int x;              /* xはint型の変数 */
    x = 5;              /* 変数xに5を代入 */

    printf("x:%d", x);  /* xの値を表示 */
    return 0;
}

 xというのが変数です。変数とは、数値や文字を格納するための「箱」みたいなものでありオブジェクトです。
 ところでこのオブジェクトは、当然作ったからには5を代入することができたり、その変数の値を表示できたりと色々なことができますが、このオブジェクト自体そもそもどこに存在するのでしょう?
 正解はメモリです。メモリとは、広大な空間を持つ記憶域であり、その中の1点にオブジェクトが存在しています。広大な空間の中の1点に存在するわけですから、このオブジェクトを端から端まで探していくのは極めて大変な作業です。そこでアドレスという、いわば住所がその1点についています。一度ここで図を使って整理しましょう。
図1.001.jpeg
 この図では広大なメモリの中の1点のアドレス「0x7ffeed0dca38」にオブジェクトのxが存在し、その中に数値の5が代入されていることになります。実際にプログラムを作って確認してみましょう。

variable.c(改変)
#include<stdio.h>

int main(void)
{
    int x;              /* xはint型の変数 */
    x = 5;              /* 変数xに5を代入 */

    printf("x:%p", &x);  /* xのアドレスを表示 */
    return 0;
}

 このプログラムをコンパイルし実行すると、おそらく人それぞれで異なる値が表示されると思います。私の場合はx:0x7ffeed0dca38になりました。
 簡単にプログラムの解説をすると、printfによる表示の際の「&」はアドレス演算子と呼ばれるものでxのメモリ上のアドレス値を表します。そして、これを表示するためには%pを使って指定したオブジェクトのアドレスを出力します。

 さて、ここまで色々と書いてきましたが、肝心のポインタの話に戻りましょう。最初の方でポインタはオブジェクトのアドレスを指し示しているということを書きました。上の図において、オブジェクトxのアドレス0x7ffeed0dca38を指し示しているのは、緑の矢印(→)ですね。ポインタはオブジェクトのアドレスを格納していることになります。イメージはこんな感じです。
図2.001.jpeg
 少し情報量が多いですね。整理しましょう。オブジェクトxがありますね。これのアドレス0x7ffeed0dca38ポインタ変数と呼ばれる箱に格納します。このポインタ変数を図ではp_xとおいています。
 このp_xは当然ながらオブジェクトxのアドレス0x7ffeed0dca38を持っているため、この情報を表示することができます。これに加えて、このポインタ変数が指し示している1点のオブジェクトx格納値も表示することができるのです。xの格納値5を出力するためには、*p_xのように米印(アスタリスク)を置きます。プログラムで確認してみましょう。

pointer.c
#include<stdio.h>

int main(void)
{
    int x;                      /* xはint型の変数 */
    x = 5;                      /* 変数xに5を代入 */

    printf("   x:%d\n", x);     /* xの値を表示 */
    printf("  &x:%p\n", &x);    /* xのアドレスを表示 */

    int *p_x;                   /* p_xはポインタ変数 */
    p_x = &x;                   /* ポインタ変数p_xにxのアドレスを代入 */

    printf(" p_x:%p\n", p_x);   /* p_xの値を表示 */
    printf("*p_x:%d\n", *p_x);  /* *p_xの値を表示 */
    return 0;
}
/*----実行結果------
   x:5
  &x:0x7ffeed0dca38
 p_x:0x7ffeed0dca38
*p_x:5
---------------- */

 ポインタが難しいと呼ばれる所以はこのプログラムに詰まっています。上2つの実行結果は先ほどのプログラムと全く同じです。ポインタ変数は普通の変数同様に宣言ができますが*マークがつきます。つくったp_xには先ほどの図のようにオブジェクトxのアドレスを代入します。このとき、*p_xではなくp_xに代入しています。下のprintfにて出力される結果をみてみましょう。p_xにはオブジェクトxのアドレス*p_xにはオブジェクトxの格納値が出力されます。

 &*の区別がしづらいので整理しましょう。

演算子 名前 説明
& アドレス演算子 オブジェクトのアドレスを取り出す
* 関接演算子 それが指し示すものの中身

 p_xはオブジェクトxを指し示すポインタ変数であり、アドレスが格納されています。*p_xという記述にするとその指し示したオブジェクトの中身、すなわち格納された5を得ることができます。

実は...

 先ほどのプログラムにおけるポインタ変数p_xの宣言ですが、実は下記の記述でも全く問題がありません。寧ろ理解がしやすいと思います。

    int* p_x;
    p_x = &x;

 int *p_xという書き方だと、int型なの?と困惑された方が多いかと思います。確かにp_xという変数はint型のオブジェクトのアドレスを指し示すデータなので、本来であればint* p_xと書くべきです。しかし、一行で複数の変数を宣言しようとした時に、その書き方だとかえって理解し難くなってしまうので、int *p_xが一般になりました。

    int* p_x, p_y;
    /* int*型のp_x, int型のp_yを用意してしまう */

最後に

 今回は導入編なので、ここまでにしておきたいと思います。ポインタはまだまだ奥が深く難しいです。なるべく早めに記事にしたいと思います。完成次第、下記にリンクを貼っておきます。

63
37
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
63
37