&演算子、*演算子、NULLポインタ、swap関数の理解度を確認しましょう。
int a = 42; int *p = &a; printf("%d\n", *p);
p は a のアドレスを持つポインタ。*p で p が指す先の値 = a の値 = 42 を取得します。int a = 10; int *p = &a; *p = 20; printf("a = %d\n", a);
*p = 20 は p が指す先(= a)に 20 を代入。a の値が書き換わるので a = 20。int *p = NULL; printf("%d\n", *p);
void swap(int *a, int *b) { int tmp = *a; *a = *b; *b = tmp; } int main(void) { int x = 3, y = 7; swap(&x, &y); printf("x=%d, y=%d\n", x, y); return 0; }
*a = x, *b = y なので、tmp=3, *a=7, *b=3。int a[3] = {10, 20, 30}; int *p = a; printf("%d\n", p[1]);
a は式の中では先頭要素へのポインタとして扱われます。
p = a で p は a[0] を指すp[1] は *(p + 1) と同じで、a[1] = 20[] が使えるのが、ポインタと配列の密接な関係を示しています。
int a[4] = {1, 2, 3, 4}; int *p = a; p++; printf("%d\n", *p);
++ は「指している型のサイズ分」アドレスを進めます。
p++: p は a[1] を指すようになる(int 4バイト分進む)*p = a[1] = 2void safe_print(int *p) { if (p != NULL) { printf("%d\n", *p); } else { printf("NULL\n"); } } int main(void) { safe_print(NULL); return 0; }
if (p != NULL) でガードしているため *p は実行されない*p を実行すると Segmentation Fault になります。安全なコードを書くうえで必須のイディオムです。
int *danger(void) { int x = 42; return &x; // ローカル変数のアドレスを返す } int main(void) { int *p = danger(); printf("%d\n", *p); return 0; }
x は、関数が終わるとスタックから消滅します。
int x = 1, y = 2; int *p = &x; int *q = &y; p = q; *p = 99; printf("x=%d, y=%d\n", x, y);
p = q は「p が q と同じアドレスを指すようにする」操作(値のコピーではない)。
*p = 99 で y に 99 が代入されるp = q(ポインタの値の代入)と *p = *q(指している値のコピー)は全く別の操作なので注意。