アドレスの計算

ポインタの中身は、変数のアドレスを指し示す値です。値の形式についてはシステムに依存するためC言語によって定められているものではありませんが、データが保存されているメモリの場所を表す何らかの数であることには間違いありません。よって、ポインタは通常の数値と同じように算術演算子で計算をしたり、等価演算子で比較できます。

もちろん、無秩序な演算に意味はありません。ポインタが指すアドレスは、アプリケーションに割り当てられた利用可能なメモリを指していなければなりません。アドレスの計算は、連続したメモリ領域を持つデータに対する捜査に役立ちます。たとえば、配列です。

配列を宣言すると、その配列の要素としてデータを保存するために必要なメモリが、連続したアドレスで割り当てられます。char 型の配列 ary が存在する場合、ary[0] のアドレスが配列の先頭を表し、ary[1] のアドレスは必ず ary[0] のアドレスに 1 を加えたものと一致します。つまり、以下の式が成り立ちます。

(&ary[0] + 1) == &ary[1]

このようなアドレスの性質を理解することは、ポインタを理解するにも配列を理解するにも重要です。結局、配列の本質は連続して確保されたメモリ領域であり、配列の先頭へのポインタからアドレスを計算することで、目的の要素にアクセスしているのです。配列型の変数 ary に保存されている値は、配列の先頭へのポインタです。つまり、以下の式が成り立ちます。

ary == &ary[0]

まず、配列の各要素のアドレスをprintf()関数で表示して、アドレスの関係を調べてみましょう。

Sample05

#include <stdio.h>

int main(void)
{
    char ary[2];

    printf("ary=%p\n", ary);
    printf("&ary[0]=%p\n", &ary[0]);
    printf("&ary[1]=%p\n", &ary[1]);

    return 0;
}

実行結果

サンプル05の実行結果

Sample05は、char 型の配列 ary を宣言しています。この配列のサイズには 2 を指定しているため、ary[0] と ary[1] の 2 つの要素を保存できます。printf() 関数で、配列 ary の値と、ary の各要素 ary[0] と ary[1] のアドレスを取得して表示しています。まず ary と &ary[0] の値が同じであることに注目してください。配列の識別子だけを指定した場合は、その配列の先頭の要素(すなわち ary[0])へのポインタとなります。また、&ary[1] のアドレスが &ary[0] のアドレスに 1 を加算したものであることにも注目してください。

この性質を利用して、配列へのポインタを算術演算してアクセスする要素を選択できます。事前に大きなデータ保存領域(バッファ)を確保して、必要な時に必要な量だけアプリケーションで利用するといった技法に応用できます。