コース目次 / 第1章

変数はメモリのバイト列 — アドレスとポインタ

printf %pで変数のアドレスを実際に表示し、sizeofでバイト数を確認します。ポインタが「アドレスを値として持つ変数」であることを、&と*の対応で理解します。

第1章 / 全6章目安 約10分この章のゴール: 変数のアドレスとサイズを実際に表示し、ポインタの基本を理解する

まず、変数のアドレスを実際に見ます。下を addr.c として保存します。

#include <stdio.h>

int main(void) {
    int x = 42;
    int y = 7;
    printf("xの値: %d, xのアドレス: %p\n", x, (void*)&x);
    printf("yの値: %d, yのアドレス: %p\n", y, (void*)&y);
    printf("intのサイズ: %zu バイト\n", sizeof(int));
    return 0;
}
gcc -o addr addr.c
./addr
xの値: 42, xのアドレス: 0x7fffe40bbbf0
yの値: 7, yのアドレス: 0x7fffe40bbbf4
intのサイズ: 4 バイト

&x の & は「このアドレスを教えて」という演算子です。%p は、そのアドレスを16進数で表示します。
実行するたびに具体的な数値は変わりますが(OS が毎回メモリの配置を少しずらす、ASLR という仕組みのため)、xとyのアドレスの差が、intのサイズ(4バイト)ぴったりになっているのが分かるはずです。xの後ろに、yが4バイトずれて並んでいる——これが「変数はメモリ上に、決まったバイト数で並んでいる」ということです。

ポインタ — アドレスを、値として持つ変数

ポインタとは、アドレスそのものを値として持つ変数です。& と * は、対になった操作です。

#include <stdio.h>

int main(void) {
    int x = 42;
    int *p = &x;  // p は「xのアドレス」を値として持つ
    printf("xの値:        %d\n", x);
    printf("pの値(xのアドレス): %p\n", (void*)p);
    printf("*p(pの指す先の値):  %d\n", *p);
    *p = 100;  // pの指す先(=x)を書き換える
    printf("書き換え後のxの値: %d\n", x);
    return 0;
}
gcc -o ptr ptr.c && ./ptr

int *p = &x; で、p には x のアドレスが入ります。
*p(アスタリスク)は「pが指す先の値」——つまり x そのものを読みます。
p = 100; とすると、p が指す先(x)が書き換わります。& はアドレスを取り、 はアドレスの中身を読み書きする——この対の関係が、ポインタのすべての基本です。

持ち帰る一言

変数は、アドレスとバイト数の組。ポインタは、そのアドレスを値として持つ変数。 &(アドレスを取る)と *(アドレスの中身を読み書きする)は対の操作。この見方が、次の章の配列、そしてバッファオーバーフローの理解につながります。

こうなっていればOK

卒業まであと4章です。

この章はまだ完了していません。