ポインタ型とは
- メモリー上のアドレスを記憶する変数の型のこと
- ただしポインタ型は派生型といって単独では存在できずInt型へのポインタ型、String型へのポイント型と言ったように他の型と合わせて作られる。これは変数型によってメモリーに格納するサイズ(場合によっては2進数の読み方まで)が異なるため、値を取り出すのにどの型の変数のアドレスかがわかる必要があるから
ポインタ型変数とは
- ポインタ型で宣言された変数のこと。この変数には、その元となった型の変数のアドレスを自由に代入できる。
更に、記憶しているアドレスのメモリを読んだり書き換えたりすることが出来る。 - ポインタ型変数として扱うポインタ変数モードと通常の変数として扱う通常変数モードの2つのモードを持っている
ポインタ型の宣言
ポインタ型にしたい変数の前に*をつけて宣言します
int p //int型
int* p //int型へのポイント型
int *p //これでもint型へのポイント型になるが後ほど説明する通常変数モードにするために変数の前につける*と混同してややこしいため今回は使いません
注意点としてこのように宣言した場合はpはポインタ型、qは通常のint型となります。pの前には*があるがqの前には*がついていないためです。
int* p, q
◎saka1029さんにご指摘いただき修正しました。ありがとうございます。
ポインタ変数モード
ポインタ変数モードは基本的にはアドレスの記憶が主な機能です
int main(void)
{
// int型へのポイント型変数
int* p = NULL;
// int型変数
int i;
i = 10;
// iのアドレスを取り出してpに代入(&演算子を使ってiのアドレスを取り出している)
p = &i;
// pの値
printf("p = %p\n",p);
// iのアドレス
printf("&i = %p\n",&i);
return 0;
}
結果
p = 0x7fff4670e844
&i = 0x7fff4670e844
int型へのポインタ型のpにint型のiのアドレスが代入出来ていることがわかります
通常変数モード
通常変数モードにするにはポインタ型変数の前に*をつけます
(宣言時につけている*とは同じ記号ですが全く意味が違うので注意が必要です)
int main()
{
// int型へのポイント型変数
int* p;
// int型変数
int i;
i = 10;
// iのアドレスをpに代入
p = &i;
// pを通常変数モードにして20を代入
*p = 20;
// pの通常変数モードでの値
printf("p = %d\n",*p);
// iの値
printf("i = %d\n",i);
return 0;
}
結果
p = 20
i = 20
iのアドレスが保存されているpを通常変数モードにして20を代入したことで同じアドレスを参照しているiの値も書き換わっていることがわかります。
汎用ポインタ
また、上記のポインタとは違い汎用ポインタと呼ばれる特殊なポインタを2つご紹介します
voidポインタ
voidポインタは単独で存在できるポインタです。
この型はどんな変数のアドレスでも記憶することが出来ますが型がわからないので値をvoidポインタ型から直接取り出すことはできません。参照するには必ずキャストする必要があります。
#include <stdio.h>
void outNumber(void *);
int main() {
int i = 65 ;
outNumber(&i);
//どんな型でも渡せる
outNumber("hogefoobaz");
}
void outNumber(void* number) {
int* num = (int*)number;
printf("%i\n" , *num);
}
結果
キャストして値が取得出来ているのがわかります。
"hogefoobaz"の方で大きなint値が返っているのは"hogefoobaz"が指すアドレスから4バイト取ってきてその値をintに変換した、つまりキャストした結果です。
65
170127754
idポインタ(id型)
Objective-Cで使えるidポインタという汎用ポインタもあります。こちらもvoidポインタ同様どんな型でもセットすることができます。idポインタでは宣言時の*は不要です。取り出す際にはvoidポインタと同様キャストする必要があります
id obj = @"foo";
NSString *str = (NSString*)obj;
◎ tomohisaotaさんにご指摘いただき修正しました。ありがとうございます。
使いみちは?
上記の例は動作を示すためのものであって実際には直接変数を操作したほうが手っ取り早いです。
ポインタの本当の使いみちはショートカットにあり、ポインタ変数に変数のアドレスを記憶しておくことで、別の場所で元の変数が使えない場所であってもポインタ変数を通常変数モードに切り替えることで元の変数と同じく使うことができるということです。
仕組みは理解出来たのですが、ショートカットとしての機能というところがいまいちイメージがつかなかった部分がありました。
詳しい方いらっしゃればコメントいただけますと幸いです。
このエントリーはこちら
を自分なりに短くまとめたものですのでより詳しく知りたい方はこちらをご覧ください
追記
ポインタ変数をconstで宣言した際の挙動について
補足情報としてポインタ変数をconstと組み合わせて宣言した場合少しおもしろい挙動をするので説明します
表にまとめるとこのようになります
宣言 | 参照先の値 | pの値 |
---|---|---|
const int *p | ✕ | ◎ |
int * const p; | ◎ | ✕ |
const int * const p | ✕ | ✕ |
◎ 変更可能 ✕変更不可
const int *p の場合
constを変数型の前にのみ書いた場合、constの宣言はintのみにかかるためポインタ変数pの値は変更することができますが、pを通常変数モードにして参照先の値を書き換えようとするとエラーになってしまいます。
int main()
{
const int *p;
int number_a = 0;
int number_b = 5;
p = &number_a;
p = &number_b; //pの値を書き換えるのはOK
*p = 3; //コンパイルエラー
}
error: assigment of read-only location '*p'
*p = 3
^
int * const p の場合
constをポインタ変数の前にのみ書いた場合、constの宣言はポインタ変数のみにかかるため、pを通常変数モードにして参照先の変更することは出来ますが、ポインタ変数pの値を変更しようとするとエラーになってしまいます。
int main()
{
int * const p;
int number_a = 0;
*p = 3; //OK
p = &number_a; //エラー
}
error: assigment of read-only location 'p'
p = &number_a; //エラー
^
const int * const p
constを変数型の前とポインタ変数の前両方に書いた場合、constの宣言は両方にかかるため、pを通常変数モードにして参照先の値、ポインタ変数pの値どちらも変更しようとするとエラーになってしまいます。
int main()
{
const int * const p;
int number_a = 0;
*p = 3; //エラー
p = &number_a; //エラー
}
error: assigment of read-only location '*p'
*p = 3; //エラー
^
error: assigment of read-only location 'p'
p = &number_a; //エラー
^
まとめると
const intで参照先の値を固定
- const pでポインタ変数の値を固定
ということです
ポインタ変数の値が参照先の値のアドレスであることを考えると分けて固定されるのもイメージがつきやすいですね。
◎こちらはshiracamusさんにコメントいただき追記しました。ありがとうございます。