C++ 基礎
首都大学東京 田川研究室
戻る

ポインタ

ポインタといえば多くの方が習得に苦労するそうですが、利用方法を知らないからだと私は思います。
ポインタを何に使うのかといえば、データの受け渡し効率を良くすることで、メモリ操作を覚えることと等しいです。メモリ操作は危険なのでほかの言語にはあまり搭載されていない機能ですが、C言語の汎用性が高い理由はメモリ操作ができるからです。なのでOSの作成や、大規模演算に使われます。

Cの入門といえば、配列から教え、その後ポインタという流れになりますが、私はこの手順では混乱を招くと考えます。
ある程度、四則演算や関数呼び出しになれてもらったらメモリ操作を覚えるのが良いかと。その後に配列操作、文字列操作、ファイル操作を覚えるのが筋です。これらの機能にはすべてメモリ操作を用います。
まず、以下の例。OSにメモリを要求します。
#include <stdio.h>
int main()
{
    int *pia;

    pia = new int[100];

    pia[0] = 2;
    pia[1] = 3;
    pia[2] = 4;

    printf("%d\n" , pia[2]);
    printf("%d\n" , pia[3]);//こちらはまだ何も入れていないので変な値が表示されるはず
    return 0;
}

new int[100];
これでOSにint型の値が、100個連続で入るだけのメモリを探してもらい、そのメモリの先頭の場所をpiaで受け取ります。int型は通常4バイトですので、これを呼び出すとメインメモリが4*100バイト消費されます。100MByteくらい確保すれば、タスクマネージャーからメモリが消費されたことを確認できると思います。

int  *pia;
メモリの場所を指し示す変数には * をつけます。メモリの場所とはコンピュータ用語でアドレスといいます。アドレスを指し示すのでポインタと呼びます。ポインタ変数には変数名の接頭語として p をつけるのがマナーです。


pia[0] = 2;
[]は添え字演算子と呼ばれます。アドレス計算を行い、メモリに入っているデータを適切な型として解釈し、適切な場所へ値を格納したり、その逆の参照ができます。
添え字演算子の動作として重要なものが二つ、アドレス計算とデータの解釈として出てきました。まずはアドレス計算を見ていきましょう。
pia[0] と書いた場合、pia の開始アドレスから0番目のint要素になります。pia[1]なら1番目、つまり4バイト進んだ位置を指すことになるのです。
ここでコンピュータのメモリアドレスとは、0Byteから始まり、搭載メモリ量が4GBなら40億程度のアドレスまであります。アドレスとは先頭から数えた数です。pia[2]の動作は、pia のアドレスにintですから 8 を足したアドレスを計算し、そのメモリを参照するということになります。

ですのでアドレスの型なんていうよくわからない変数型が存在します。つまり、添え字演算をするときに1要素が何バイトなのかを判断するためにあります。

データの解釈とは、同じ 4 という数値でも、intfloatではまるで違うということです。コンピュータで数値を表すにはbitの組み合わせで表現をしますが、この表現の仕方に浮動少数点数フォーマット、整数フォーマットの2つがあります。現在主流のCPUにほかのフォーマットを処理できるものは無いと記憶しています。

1991をint型であらわすと、そのbit列は以下のようになります。

int 1991   = 0000 0000 0000 0000   0000 0111 1100 0111
16進数 = 0x00000c7c

対するfloatは以下のようになります。

float 1991 = 0100 0100 1111 1110   0100 1110 0000 0000
16進数 = 0x44fe8e00

まあ似ても似つきませんね。つまり、実際にメモリへ代入するときに、同じ数値を代入しようとしても型の違いによってbit列を変えなければいけないわけです。添え字演算でアドレス計算された場所に値を代入するときにこの操作を行います。よってそのメモリがどのデータ型として使われるかをコンパイラが識別するためにアドレスの型が存在します。

アドレスの型とは上記二つの理由のために必要となります。この概念が無い時代はさぞ大変だったと思います。アドレスに種類なんて無いのですから、書きながら自分で覚えていないといけません。ありがたい機能です。ぜひ使いこなしたいところですね。
次に、関数の引数として配列のアドレスを渡す例を示します。100個の要素を渡すのに、アドレスだけ渡せばすむのです。
#include <stdio.h>
void setfunc(int *piset , int size)
{
    int ic=0;
    while(ic < size)
    {
        piset[ic] = 2;
        ic ++;//ic = ic+1 と同義
    }
}
//ic++; はインクリメント命令で最速。いまは関係ないですが
int main()
{
    int *pia;

    pia = new int[100];
    
    setfunc(pia , 100);

    //すべて2が出力されるはず
    printf("%d\n" , pia[2]);
    printf("%d\n" , pia[3]);
    printf("%d\n" , pia[33]);
    printf("%d\n" , pia[60]);
    return 0;
}
処理が簡単に、理にかなったものとなります。