プログラミング演習II

「ポインタの基礎」

配列とアドレス

C言語では、配列変数は変数名自体が先頭要素のアドレスを表すことになっている。
したがって、以下のコードのように、&を使わなくても各要素のアドレスを参照することができる。

#include <stdio.h>

int main(void)
{
    int xs[5] = {1, 2, 3, 5, 8};
    printf("%p\n", xs);    /* &xs[0] と同じ*/
    printf("%p\n", xs+1);  /* &xs[1] と同じ*/
    return 0;
}

配列とポインタ

上記の性質とポインタを合わせると、以下のサンプルコードのように複数の方法で配列の要素にアクセスできる。

  1. "xs[i]" : 通常の配列の参照を行なう記述法
  2. "*(pi + i)" : ポインタ変数にオフセットをつけて値を参照する方法であり、ポインタ変数が指すアドレスから"i"番目後方にあるデータの値を参照する。
  3. "pi[i]" : ポインタ変数を用いたもので、"pi"はポインタ変数であり、配列変数ではないが、ポインタ変数を配列変数と同じように扱っている。
#include <stdio.h>

int main(void)
{
    int i;
    int xs[5] = {1, 2, 3, 5, 8};
    int *pi;

    pi = xs;    /* &xs[0] と同じ*/

    for (i = 0; i < 5; i++)
	printf("xs[%d]=%d  pi[%d]=%d  *(pi+%d)=%d  *(xs+%d)=%d\n",
	       i, xs[i], i, pi[i], i, *(pi+i), i, *(xs+i));

    return 0;
}

実行結果は以下のようになる。どの方法を用いても同じ値が参照される。

xs[0]=1 *(pi+0)=1 pi[0]=1  
xs[1]=2 *(pi+1)=2 pi[1]=2  
xs[2]=3 *(pi+2)=3 pi[2]=3  
xs[3]=5 *(pi+3)=5 pi[3]=5  
xs[4]=8 *(pi+4)=8 pi[4]=8

以下の図は上記サンプルプログラムにおいての、ポインタと配列の関係を示したものである。
ポインタ変数 pi に配列の先頭アドレスを代入した後(つまり、pi = xs)は、間接演算子*を使った表現やポインタ変数の配列のような表現を使うことができる。

ポインタと配列の関係その1

ここで、もしポインタ変数の値を1だけ大きい値に変更した場合、ポインタ変数が指す配列の要素は配列の先頭ではなく、隣の要素を指すようになる。つまり、配列中のある特定の要素を直接指し示すことができる。
(そのため、同じ配列要素を参照するためには、ポインタ変数に対するオフセット値(あるいは添字の数)をそれぞれ1つずつ小さくする必要がある)

ポインタと配列の関係その2

このようにポインタと配列は同じように使うことが可能である場合もあるが、全く同じものではない。例えば以下の例で、ポインタ変数は値の書き換えが出来るが、配列変数は書き換えができない。

int xs[5] = {1, 2, 3, 5, 8};
int *pi = xs;

pi = pi + 1;           /* OK (ポインタ変数は書き換えられる) */
xs = xs + 1;       /* エラー (配列変数は書き換えができない) */

Exercise 3: 配列とポインタ

array-ptr-test.cをコンパイル・実行し、これまでに説明した配列とポインタの関係を確認してください。