これはメモリ上に int 型のデータを格納する場所を一つ確保し、 a という名前を付けて、 そこに 100 を格納します。int a; a = 100;
実際には、 メモリのひとつひとつの「記憶単位」に番号が付けられています。 この番号のことを「番地(address)」と呼びます。 ポインタはこの番地のような、 データの格納場所を示す値をデータとして取り扱うデータ型です (なんちゅう説明だ…)。
図では変数 a は8番地のメモリの内容を指しています。 変数 a の格納場所を示すポインタを求めるには、 & という単項演算子を使います。
なお、この場合 &a は定数です(変数 a の格納場所は固定だからね)。 これをポインタ定数と呼びます。&a
int *pa; という宣言によって、 pa という int 型のポインタ変数が用意されます。 これは「*pa」が int 型であるという宣言であって、 * が取れた pa が「int 型のポインタ変数」になります。int a; int *pa; a = 10; pa = &a;
ポインタ変数が保持するデータはポインタ(番地)であり、 その場所に格納されているデータを取り出すためには、 * という単項演算子を用います。 * は掛け算と同じ記号ですが、 ポインタ型の変数に対する単項演算子として使用した場合は、 ポインタによって示されている場所に格納されているデータを取り出します。
ポインタの値は、 オペレーティングシステムによってプログラムに割り当てられたメモリ空間内の、 どこか一箇所を指していなければなりません (どこも指していないということを明示するために 0 という値も用います)。 * 演算子によってその内容を操作しようとする場合は、 そのポインタが変数のようにデータを記憶する目的で割り当てられた メモリの領域を指していなければなりません。 もし不正な内容のポインタを * 演算子によって操作しようとすれば、 プログラムは正常に動かないか、異常終了します。int a, b; int *pa; a = 10; pa = &a; b = *pa; /* b に 10 が代入される */
配列の各要素はメモリ上に連続して配置されているので、 2つ目の要素 a[1] のポインタは、 次のようにして求めることができます。int a[10]; int *pa0, *pa1; pa0 = &(a[0]); /* a[0] のポインタ */
従って、a[0] および a[1] の内容は、 それぞれ、次のようにして参照できます。pa1 = pa0 + 1; /* a[1] のポインタ */
++ や -- という演算子もポインタ変数に使用することができます。
* は ++ より優先順位が低いため、 上の例では c に a[0] の内容が入り、 その後 pa0 の値は &(a[1]) になります。 もし *pa(すなわち a[0])の内容を1増すなら、 ( ) を使う必要があります。int a[10]; int *pa0, c; pa0 = &(a[0]); /* a[0] のポインタ */ c = *pa0++;
この例では c に a[0] が入りますが、 その後 a[0] の内容は a[0] + 1 になり、 pa0 の内容は変化しません。 なお、c = (*pa0)++;
この場合は a[0] の内容が1増され、それが c に代入されます (* が ++ より pa0 に近いところにあるため)。c = ++*pa0;
実は上記において宣言されている配列 a[10] の a は、
それ自体が配列として確保されたメモリ領域の先頭を指すポインタ定数、
すなわち &(a[0]) と等しい値を持ちます。
従って、
となります。すなわち、
[ ] は実はポインタに対する演算子であり、
ポインタ変数 pa0, pa1 に対しても、
次のように適用できます。
上の例では、int a[10]; int *pa0, *pa1; pa0 = &(a[0]); /* a[0] のポインタ */ pa1 = pa0 + 1; /* a[1] のポインタ */
従って、確保された領域からはずれるような 配列の添え字やポインタを使用しても、 コンパイルの際にはエラーにはなりませんし、 問題なく実行できるように見えることもあります。 しかしこれは非常に発見しにくいプログラムの異常動作の原因になりますし、 大抵は致命的なエラーを引き起こします。 ポインタを使う場合には細心の注意が必要です。
文字列の実体は配列ですから、 これもポインタになります。
文字列定数の値は、 その文字列が格納されている位置を指すポインタ定数になります。 string[0] は 's'、string[5] は '!' になります。char *string; string = "Hello!\n";
上の例は、0〜15 の値を持つ整数 i を、 それに対応する16進数の文字 c に変換します。int i; char c; c = "0123456789ABCDEF"[i];
こうすると、*(p[0] + 1) は *(a + 1) すなわち a[1] になります。 さらに [ ] はポインタに対する演算子なので、 p[0][1] と書くこともできます。 すなわち2次元配列のように扱うことができます。int *p[3]; /* ポインタの配列 */ int a[4], b[5], c[6]; p[0] = a; p[1] = b; p[2] = c;
p[0][1] = 10; /* a[1] = 10; */ p[1][2] = 20; /* b[2] = 20; */ p[2][3] = 30; /* c[3] = 30; */
引数に変数のポインタを使うことによって、 呼び出した関数から変数に値を戻すことができます。
#include <stdio.h> void add10(int *a) { *a += 10; /* 引数のポインタが指す内容に 10 を足す */ } int main(void) { int x; x = 20; add10(&x); printf("x = %d\n", x); /* 30 になっている */ return 0; }
配列の場合は、配列名がポインタ定数なので、 それをそのまま指定すればポインタを渡すことができます。
しかし、 ポインタとして渡されるのは配列の先頭の要素の位置だけなので、 配列の大きさはこの例のように別に渡してやる必要があります。
仮引数の次のいずれの形で宣言しても構いません。
#include <stdio.h> void alladd10(int a[], int n) { int i; /* 配列の全ての要素に10を足す (/ for (i = 0; i < n; i++) { a[i] += 10; } } int main(void) { int x[20]; alladd10(x, 20); return 0; }
ただし、2次元以上の配列の場合は、 最も左の添え字以外はサイズを省略できません。
これは、 多次元配列も メモリ上は1次元に展開されており、 それがどう分割して使用されているかを明らかにする必要があるからです。
#include <stdio.h> void alladd10(int a[][3][2], int n) { int i, j, k; /* 配列の全ての要素に10を足す (/ for (i = 0; i < n; i++) { for (j = 0; j < 3; j++) { for (k = 0; k < 2; j++) { a[i][j][k] += 10; } } } } int main(void) { int x[4][3][2]; alladd10(x, 4); return 0; }
ポインタの配列は、 次のようにして関数に渡すことができます。
ポインタの配列の仮引数の宣言は、 次のいずれでも構いません。
#include <stdio.h> void alladd10(int *a[], int *n, int m) { int i, j, k; /* 配列の全ての要素に10を足す */ for (i = 0; i < m; i++) { for (j = 0; j < n[i]; j++) { a[i][j] += 10; } } } int main(void) { int *x[3], n[3]; int a[4], b[5], c[6]; x[0] = a; n[0] = 4; x[1] = b; n[1] = 5; x[2] = c; n[2] = 6; alladd10(x, n, 3); return 0; }
もちろん、関数のポインタの配列、なんてものも使えます。
int add(int x, int y) { return x + y; } int sub(int x, int y) { return x - y; } int main(void) { int x; int (*func)(int, int); /* 関数のポインタ変数の宣言 */ func = add; x = (*func)(2, 3); /* 関数 add が実行される */ func = sub; x = (*func)(2, 3); /* 関数 sub が実行される */ }
int add(int x, int y) { return x + y; } int sub(int x, int y) { return x - y; } int main(void) { int x; int (*func[2])(int, int) = { add, sub }; /* 関数のポインタ配列 */ x = (*func[0])(2, 3); /* 関数 add が実行される */ x = (*func[1])(2, 3); /* 関数 sub が実行される */ }