c-lang:ポインタ
ポインタ(pointer)
被参照型の実体を間接参照可能なオブジェクト。
オブジェクト(のアドレス)を指すオブジェクトであり、関数(のアドレス)を指すオブジェクトである。
ポインタのポインタとは、オブジェクト(のアドレス)を指すポインタ(のアドレス)をさすオブジェクトのことである。
ポインタは、一種のアドレスであるが、絶対的なものか、相対的なものかといった問題は、すべて処理系に依存する。
ポインタを初期化せずにポインタの増分や間接参照をした場合の動作は不定である。
ポインタの初期化は、しばしば忘れがちであり、注意する必要がある。
(重要)ポインタは「アドレスを記憶している変数」である。
ポインタ型(pointer type)
被参照型の実体を間接参照可能である値をもったオブジェクトの型。
被参照型から派生される。
void へのポインタは、文字型へのポインタとして、同一の表現と境界整列条件を持っている。
他の型へのポインタは、同一の表現や境界整列条件とは限らない。
「~へのポインタ(pointer to ~)」
「~のポインタ」
「~ポインタ」
「~ *」
という表現は、いずれも、
「被参照型が~型の実体へのポインタ型」
「~型の被参照型の実体へのポインタ型」
「~型の被参照型が派生するポインタ型」
「被参照型~によって派生するポインタ型」
という意味である。
ポインタの使い方の定石
1. ポインタ変数を用意する
2. ポイント先のオブジェクトを用意する
3. オブジェクトへのポインタ値をポインタ変数に格納する
4. ポインタを通じてオブジェクトを操作する
配列をポインタを使って操作する
code:cpp
char str5 = "abcd";
char *p
p = str ; /* p == str0 == 'a' */
p = str + 1; /* p == str1 == 'b' */
p++; /* p == str2 == 'c' */
p = &str3; /* p == str3 == 'd' */
str = p; /* 代入できない */
ポインタを添え字を使って配列のように扱う
code:cpp
char str5 = "abcd";
char *p
p = str ; /* p == str0 == 'a' */
printf("%c%c%c%c\n",p3,p2,p1,p0);
ポインタの演算
ポインタは、変数などの存在する位置を指す(point)するもの(pointer) である。
メモリのアドレスは全て整数で表現しているので、 ポインタは整数の一種となる。
ポインタ変数は、変数なので、代入、足し算引き算などの演算 の対象とすることができる。
ポインタは整数との加減算だけが許されている。
乗除算はできない。
また、ポインタが未定義であることを意味する定数 NULL の代入が許されている。
ポインタ間の四則演算を行うことはできない。
ある変数のポインタを得るためには&演算子を用い、 ポインタから変数の実体を得るためには*演算子を用います。
ポインタの比較
意味のあるポインタ比較
1. ポインタとNULLとの比較
2. 同一要素の任意の要素を指している2つのポインタの比較
ポインタは実際のところ何の役にたつのか
動的に割り当てた配列
複数ある類似の変数への汎用アクセス
関数引数の(擬似)参照渡し
あらゆる種類のデータ構造を動的に割りつける。特に木構造やリンク付きリスト
配列の中を動いて回る(例えば、文字列を解析するとき)
配列や構造体の効率のよい参照渡しの「コピー」。とくに関数引数の場合。
などなど
ポインタの配列
ポインタは配列にすることもできる。
code:cpp
char *str10;
二次元配列とポインタの配列
code:cpp
static char week710 = {
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
"Sunday",
}
table:week
week[0][0] M o n d a y \0 - - -
week[1][0] T u e s d a y \0 - -
week[2][0] W e d n e s d a y \0
week[3][0] T h u r s d a y \0 -
week[4][0] F r i d a y \0 - - -
week[5][0] S a t u r d a y \0 -
week[6][0] S u n d a y \0 - - -
code:cpp
static char *week7 = {
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
"Sunday",
}
table:week1
week[0] ポインタ → (1) へ
week[1] ポインタ → (2) へ
week[2] ポインタ → (3) へ
week[3] ポインタ → (4) へ
week[4] ポインタ → (5) へ
week[5] ポインタ → (6) へ
week[6] ポインタ → (7) へ
table:week2
(1) のアドレス→ M o n d a y \0
(2) のアドレス→ T u e s d a y \0
(3) のアドレス→ W e d n e s d a y \0
(4) のアドレス→ T h u r s d a y \0
(5) のアドレス→ F r i d a y \0
(6) のアドレス→ S a t u r d a y \0
(7) のアドレス→ S u n d a y \0
ポインタのポインタ
code:cpp
char a; /* aはchar 型の変数である */
char *b; /* bはchar 型の変数を指すポインタである
* 「*b」という記述が、char型である
*/
char **c; /* cはchar 型の変数を指すポインタを指すポインタである
* 「*c」という記述は、「**c」を指すポインタである
* 「**c」という記述が、char型である
*/
ポインタのポインタのポインタ
当然できる。
char ***d;
ポインタのポインタのポインタのポインタ
できるが、いったいどんな時に使うんだ? っていうか、typdef しろよ。
ポインタのポインタのポインタって、使ったら嫌われそうだ。
code:cpp
#include <stdio.h>
int main()
{
int a = 0;
int *p = &a;
int **pp = &p;
int ***ppp = &pp;
scanf( "%d", &a );
printf( "a = %d\n", a );
printf( "*p = %d\n", *p );
printf( "**pp = %d\n", **pp );
printf( "***ppp = %d\n", ***ppp );
return 0;
}
ポインタによる関数呼び出し
関数名とは、関数本体へのポインタである。
foo()
と
(*foo)()
は同じ意味である。
配列とポインタの違い
table:arry-pointer
ポインタ 配列
データのアドレスを格納する。 データを格納する。
データは間接参照される。 データは直接参照される。
普通は、動的なデータ構造で使われる。 普通は、特定要素数の同じ型のデータの格納に使われる。
普通は、malloc() free()などと一緒に使う。 自動的に確保、削除される。
普通は、名なしのデータを指す。 それ自身、名前のついた変数である。
次の式が真になることは仕様として保証されている。
code:cpp
pi == *(p + i)