ポインタ


メモリアドレスの秘密

C言語によるプログラミング技術の中で、もっとも大切なものの一つがポインタです
一度でもC言語に触れたことがある方ならば、ポインタに対して「難しい」というイメージがあるかもしれません
実際、筆者も最初は抵抗がありました

しかし、ふたを開ければ大したことはありません
肝心なのはプログラム知識よりも、コンピュータアーキテクチャ(というほど大げさでもないが)の知識です
ポインタはメモリと深い関係があります

では、本題に入ります
ポインタとはそもそもなにか、簡単に言うと変数の一種です
これまで私たちは変数を扱ってきました。変数で文字列を扱うこともできるようになりました
ポインタも変数です。難しいことはありません

ポインタも変数なので、よくポインタ変数とも呼びます
問題は格納するものです。ポインタには変数のように定数を直接代入するわけではありません
ポインタにはメモリアドレスを代入するのです!!

変数は値をメモリのどこかに一時的に保存するものです
あらゆるデータには、それを管理するための「アドレス」が割り当てられます
郵便物を送るのに住所が必要なように、データにも住所があるのです

では、ポインタの前に変数がメモリのどこに保存されているかを実際に見てみましょう
変数のアドレスを表すには変数名の前にアンパサンド(&)をつけます
勘のいい人は思い出したかもしれませんが、scanf("%d",&var)というようにscanf()関数ですでにつかってます
このときの & 記号は変数のアドレスを関数に渡していたのです!
#include <stdio.h>

int main()
{
        char str[3] = { 'A' , 'B' , 127};
        int count;

        for (count = 0 ; count <= 2 ; count++) {
                printf("str[%d]のメモリアドレス = %x\n" , count , &str[count] );
        }
        return 0;
}
おそらく、値は違っても次ぎのような結果が得られたと思います

64fdf8
64fdf9
64fdfa

16進数で出力された結果が、配列変数str[]の各要素のメモリアドレスです
こうしてみると、配列の場合はメモリの領域の番号が順番に並んでいることがわかりますね
ただし、なぜ1ずつ増えているのかというとchar型が1バイトだからです
intだと4バイトなので4ずつ増えることになります(この辺は、後ほど詳しくやりましょう)


アドレスをポインタに代入する

メモリアドレスの取得の方法はわかりましたね
しかし、それだけでは何もできません

そこでポインタが出てきます。ポインタとはメモリアドレスを格納することができる変数でした
ポインタ変数にアドレスを代入することによって、そのポインタ変数は直接メモリアドレスにアクセスできます
遠隔から変数の内容を操作するようなイメージです

ポインタ変数の型や宣言も通常の変数同様ですが、変数名の前にアスタリスク(*)をつけます

型 *変数名

こうすると、ポインタ変数に対してアドレスを格納することができます
実際にやってみましょう
#include <stdio.h>

int main()
{
        int *po , var;
        var = 10101;
        po = &var;

        printf("ポインタに格納されている内容 = %d\n" , *po);
        printf("ポインタに格納されているアドレス = %x" , po);

        return 0;
}
注意してほしいのが、ポインタ変数を参照するときの方法です
printf()関数の部分を見てください
*poとpoの二種類の方法でそれぞれポインタ変数を参照しています

じつは、ポインタ変数名の前にアスタリスク(*)をつけて参照すると
ポインタ変数が格納しているメモリアドレスの内容を参照します

アスタリスクをつけない後者のprintf()関数の po では、格納されているメモリアドレスを指します

メモリアドレスの内容をポインタで参照できるということは
その内容に対してポインタから別のデータを上書きできるとも考えられます
元の変数をポインタを通して参照することを間接参照(indirection)と呼びます
#include <stdio.h>

int main()
{
        int *po , var;
        var = 100;
        po = &var;

        *po = 1010;
        printf("変数varの値 = %d" , var);

        return 0;
}
このプログラムを見ると、一見変数varには最初に100を代入しただけのように思えます
しかし、結果は 1010 出力されます
これは、変数varのアドレスを格納しているポインタ変数poから間接参照によって上書きされたためです

*po = 1010

ここで*poはアドレスの内容を表しています
つまり変数varの内容と同じですね。これに1010が代入されたために結果が1010になったのです

この程度のプログラムでは、何のためのポインタなのか理解できないかもしれませんが
今後、関数や構造体を使った高度なCプログラムに移ってくると、ポインタの役割が理解できるようになります


ところで、ポインタ変数にもデータ型が存在します
char型の変数のアドレスはchar型のポインタに、int型の変数のアドレスはint型のポインタに代入していました
Cコンパイラはデータ型を使ってポインタの指す変数が何バイトであるかを調べます
これは間接参照による代入や比較をするときに、何バイトコピーしたり比較したりするかを知るためです
このため、特殊なケースを除いて型の異なるポインタ変数にアドレスを代入 したりすることはありません
#include <stdio.h>

int main()
{
        char *po;
        int var1 , var2;

        var1 = 1028;
        po = &var1;     /*char型のポインタにint型の変数のアドレスを代入*/
        var2 = *po;     /*ここで間違った値がコピーされる*/

        printf("%d", var2);
        return 0;
}
またまたビットレベルのお話ですが
1028は2進数では 10 0000 0100 です
しかし、ポインタがchar型なので間接参照では8ビットまでしかコピーされません
ということは 0000 0100 すなわち10進数の4です
はい、予想どうり上のプログラムを実行すると4が出力されます
ポインタ変数が指すアドレスは確かにvar1ですが、データ型が1バイトのために1バイトまでしかコピーされません



前のページへ戻る次のページへ