ポインタという単語
ポインタは変数内の値か変数のアドレスか。
どちらを処理しているのかを抑えれば、理解できたのと等しいと語りました。
変数内の値はこれまで解説してきた通り、
int test = 10;
などど代入された値が格納されることを理解されていると思います。
では、アドレス番地はどの様に格納し、どの様な型を宣言するのでしょうか?
ポインタ型
int型、short型などこれまで登場してきた型と同じ様に、ポインタもポインタ型と呼ばれる型があります。
この方がまさしくアドレス値を格納する型になります。
一つだけ他の型と違う点があります。
それは、独立した型ではないということです。
intやshortの様にそれ自体が型として存在したものではなく、他の型と組み合わせて初めてポインタ型となります。
例を挙げてみましょう。
元の型 間接演算子 組み合わせるとポインタ型
int * int*
これまで宣言してきた型に * を付与することでポインタ型になります。
その為、他の型と組み合わせて初めてポインタ型になる。と書きました。
もちろん、int型以外の型も同じです。
char*、short*、double*・・・といった感じで * を付けることでポインタ型に変化します。
ポインタ値
これはアドレス値のことです。
上記のポインタ型に入った値をポインタ値と呼びます。
ちなみに、ポインタ値(=アドレス値)と通常の整数値を分けて扱う理由は、混乱を招くからです。
数字として見れば、アドレス値も整数値もint型やshort型などに格納することでよいと思いますが、用途が違いますので、アドレスを扱う方法と媒体、通常の値を扱う方法と媒体。
わけることで混乱を避けています。
ポインタ変数
上記のポインタ型で書いた int*で宣言された変数のことです。
例えば、int *point; と宣言した場合、pointという名前のポインタ型のポインタ変数となります。
参考書には、ポインタ変数モードと通常変数モードと書かれています。
要は、ポインタ変数はアドレスを扱うモードとこれまで出てきた通常の型と同じ様に、通常の値を扱うモードがある。ということです。
後述でも改めて解説しますが、以下のポインタ変数が宣言されているとします。
int *point;
[ポインタ変数モードの処理]
point = 10;
pointというポインタ変数にアドレス番地10番を設定します。
その為、pointのアドレス番地は10番となり、そのアドレス10番に格納されている値はわかりません。
[通常変数モードの処理]
*point = 10;
違いは * が付いているか付いていないかです。
付いている方はこれまでと同じ処理として使われます。
つまり、int型のpointという名前の変数として処理できます。
その為、*point = 10;は、pointの中身は10が格納されます。
値は10ですが、point変数のアドレス番地はわかりません。
*が無ければ、そのポインタ変数にはアドレス番地が格納される。
*が有れば、そのポインタ変数には値が格納される。
同じ 10 という値でも、アドレス番地の10なのか、値としての10なのか意味合いが違います。
その違いを変えるトリガーになるのが * の付与になります。
ポインタ変数の宣言
上記で既に解説をしましたが、注意が必要な点があります。
int *p;
int* p;
どちらも同じポインタ変数として処理が可能となります。
上記では上側の int *p の書き方で進めてきました。
それは、以下の様に複数変数を宣言した場合、ポインタ変数がどこまで宣言されているのかわかりづらいからです。
int* p1, p2;
この場合、p1、p2どちらもポインタ変数なのか、p1だけポインタ変数なのか、
迷いが生じてしまいます。
結論としては、p1だけポインタ変数でp2はint型の通常の変数になります。
その為、変数名の方に * を付記する癖をつけるようにしましょう。
アドレスを代入する
ポインタ変数には * を付ける/付けないで数値自体を格納しているのか、アドレス値を格納しているのかが変わることを説明しましたが、変数に入った値をポインタ変数へ代入する際にはどの様に記載するのか解説したいと思います。
int main(void)
{
int *p;
int i;
p = &i;
return 0;
}
int型の変数名 i には、どの様な値が格納されているかは不定です。
宣言時に初期値を入れていない場合は、パソコン側の都合でゴミデータが入れられているかもしれません。
その上で、int型のポインタ変数である *p へ i の値を代入しています。
& が付いている場合は、付記された変数のアドレスがポインタ変数に代入されます。
つまり、 p = &i; は、i のアドレス番地が p に格納されます。
ちなみに、int *p; の時点では初期値がないので、 p の中は不定な値が格納されています。
初期値がない場合、処理途中で何も代入されないままであれば、
この変数を使う時点でバグになる恐れがあります。
NULL(ヌル)
C言語には、NULL(ヌル)と呼ばれる予約語があります。
意味としては「何もない」という意味になります。
int *p = NULL;
この様に記載すると、pには何も設定されていない。という意味になります。
何も設定されていないと言いつつも、実際は 0 が格納されます。
その為、int *p = 0; と書くのと同じになります。
NULLと書かずに 0 でもいいのですが、NULLと書くことで、
アドレス値はない。ということを読み手に意図していることになります。
モードの切り替え
上記の「ポインタ変数」で解説した以下になります。
*が無ければ、そのポインタ変数にはアドレス番地が格納される。
*が有れば、そのポインタ変数には値が格納される。
参考書にも記載されていますが、C言語では * が3通りの意味を持つことになっています。
1:乗算演算子としての *
つまり、掛け算の時に使う演算子。
2:関節参照演算子としての *
つまり、ここのモード解説をしている * が有る/無いで代入値の扱いが変わる演算子。
3:ポインタ変数を宣言する際に使う *
つまり、ポインタ変数とするのか、通常の変数とするのかを決める。
前後の文脈から読み取るしかない為、これはC言語の欠陥の一つになります。