C言語の基礎
前へ 目次へ 次へ 

§3 ポインタ

 変数に代入されたデータは原則としてメモリに格納される。そして、メモリに対してはアドレスによって特定の場所を指定した。例えば、
a = 100;

は、コンピュータの内部では、次のように処理されている。

ポインタ

 上図のように変数aの内容はメモリの1000番地に格納されている。ユーザは、変数を使わずにアドレスを指定してデータを格納していっても良いわけであるが、プログラムでは多くの変数を使うから、これらすべてをアドレス(単なる数値)に置き換えたのでは、そのアドレスを見て、何のためのデータかを覚えておくのは不可能である。そこで変数名(メモリのアドレスに名前を付けたもの)ができたわけである。

 ユーザにとっては、"aという変数に100を代入する"と言うことであるが、コンピュータにとっては、"aという記号の指示するメモリ上のアドレスに100を格納する"ということをやっているわけである。このアドレスを扱うものをポインタといい、処理の高速化につながる。


3-1 ポインタ演算子

 ポインタを扱うには、その変数に格納されているデータが、実際にメモリのどのアドレスに格納されているのかを知る必要がある。例えば、
a = 100;

としたとき、この変数aが示しているアドレスが何番地であるかを知るために、アドレス演算子 &を使う。

pa = &a;

 変数paには、変数aのアドレスが代入される。すなわち変数aのデータが実際に格納されているメモリのアドレスがわかったわけである。

 次に、このアドレスの中に格納されているデータを知るために、ポインタ演算子 *を使う。

x = *pa;

 変数xには、100が代入される。

 次の例で確認してみよう。

/* sample16.c */
#include <stdio.h>
int main(void)
{
    int a, x;
    int *pa;     /* ポインタ変数の宣言 */
    a = 100;
    pa = &a;     /* 変数aのアドレスを代入する */
    printf("address=%x\n",pa);    /* 変数aのアドレスを表示 */
    x = *pa;     /* 変数aのアドレスの内容をxに代入 */
    printf("data:a=%d x=%d\n",a,x);
}
実行結果

address=XXXXXXXXXX
data:a=100 x=100



& 変数のアドレスを表す。 &a, &b
* アドレスに格納されている内容を表す。 *p, *a


3-2 ポインタの宣言

 ポインタを使用するには、ポインタ用の変数(変数のアドレスが格納される変数)が必要である。次のように宣言する。
int  *p;

 char *pp; 今までの変数宣言と同じように宣言する。ただし、変数名の直前に*を付ける。(これからは普通の変数と区別するためにポインタ用の変数をポインタと呼ぶ。)
 宣言しただけでは普通の変数と同じで、ポインタにはでたらめな内容が格納されている。したがって、どれかの変数のアドレスを代入しておかなければならない。これをポインタの初期化という。

変数aのアドレス 変数aの内容
&a
pa
a
*pa

  int a;
  int *pa;
  pa = &a;  ← ポインタの初期化


3-3 ポインタと配列

 ポインタは、配列とあわせて使用することで有効に利用することができる。
/* sample17.c */
#include <stdio.h>
int main(void)
{
    int a[4], i;
    for(i = 0; i < 4; ++i)
        a[i] = i * 10;
    for(i = 0; i < 4; ++i)
        printf("a[%d]=%d\n",i,a[i]);
}
実行結果

a[0]=0
a[1]=10
a[2]=20
a[3]=30

 ここで、データ0が格納されている変数はa[0]である。この変数a[0]のアドレスを取り出すには、
pa = &a[0];

とするとポインタpaに取り出せる。したがって、*paは変数a[0]と同じデータを表すことになる。
 a[1]のデータを取り出したいときは、a[1]はa[0]の1つ次であるからa[0]を表すポインタ変数paに1を加えたら良いようである。

*(pa + 1)

 ポインタpa(アドレスが格納されている)に1を加えてからポインタ演算子の*の処理をするように( )がつけてある。これでa[1]のデータがポインタを使って取り出せる。
 このように配列のデータはすべてポインタで表現できる。

a[0] == *pa a[2] == *(pa + 2)
a[1] == *(pa + 1) a[3] == *(pa + 3)

 上のプログラム(sample17.c)は次(sample18.c, sample19.c)のようにも書くことができる。

/* sample18.c */
#include <stdio.h>
int main(void)
{
    int a[4], i;
    int *pa;
    pa = &a[0];
    for(i = 0; i < 4; ++i)
        *(pa + i) = i * 10;
    for(i = 0; i < 4; ++i)
        printf("a[%d]=%d\n",i,*(pa + i));
}
/* sample19.c */
#include <stdio.h>
int main(void)
{
    int a[4], i;
    int *pa;
    pa = &a[0];
    for(i = 0; i < 4; ++i)
        *pa++ = i * 10;      /* *(pa)にi*10の値を代入してからpaに1を加える */
    pa = &a[0];       /* paの値が変化してしまったのでポインタを初期化 */
    for(i = 0; i < 4; ++i)
        printf("a[%d]=%d\n",i,*pa++);
}

 *(pa+1)は、paが変数なので

pa = pa + 1;

と書くこともできる。同じようにpa=pa+1は、

++pa;

と書くこともできる。

 ここで配列aとは何であろうか。次のsample20.cを実行してみよう。

/* sample20.c */
#include <stdio.h>
int main(void)
{
    int a[4];
    a[0] = 100;
    printf("%d\n",a);
}
 実行結果は、100が表示されずに訳のわからない数値が表示される。
 これは、変数a[0]の値が記憶されているメモリのアドレスである。本当に配列aの先頭アドレスなのかsample21.cを実行してみよう。
/* sample21.c */
#include <stdio.h>
int main(void)
{
    int a[4];
    a[0] = 100;
    printf("%d\n",&a[0]);
    printf("%d\n",a);
}
 実行結果は、同じ数値が表示される。すなわち次のことが言える。
「配列名とは、配列の先頭のアドレスを示す定数である。」

 したがって、

pa = &a[0];

pa = a;

と書くことができる。

 最後に、ポインタというものについて見てみよう。

/* sample22.c */
#include <stdio.h>
int main(void)
{
    int a[4], i;
    int *pa;
    pa = a;
    for(i = 0; i < 4; ++i)
        *pa++ = 1000 + i;
    pa = a;
    for(i = 0; i < 4; ++i){
        printf("a[%d]=%d pa=%d\n",i,*pa,pa);
        ++pa;       /* pa = pa + 1 */
    }
}
実行結果

a[0]=1000 pa=xxxxxx+0
a[1]=1001 pa=xxxxxx+4
a[2]=1002 pa=xxxxxx+8

 ここでpaの値に注目する。paを1ずつ増やしているはずなのであるが、表示は4つずつ増えている。なぜならばpaはint型のポインタであり、int型とは4バイトの大きさだからである。(int型の大きさが2バイトのものもある。そのときは、2ずつ増える。)
 したがって、int型のポインタを1大きくするということは、実際にはメモリのアドレスを4つ増やすことになる。


前へ 目次へ 次へ 
Copyright © 2001-2003 Hiroshi Masuda 

 

 

inserted by FC2 system