🇯🇵 日本語 | 🇺🇸 English

第35回 確認問題(ポインタ)

&演算子、*演算子、NULLポインタ、swap関数の理解度を確認しましょう。

確認問題1 ― &演算子

int a = 42;
int *p = &a;
printf("%d\n", *p);

出力結果は?

42
アドレス値
0
コンパイルエラー
解説: p は a のアドレスを持つポインタ。*p で p が指す先の値 = a の値 = 42 を取得します。

確認問題2 ― ポインタ経由の変更

int a = 10;
int *p = &a;
*p = 20;
printf("a = %d\n", a);

出力結果は?

a = 10
a = 20
a = 0
コンパイルエラー
解説: *p = 20 は p が指す先(= a)に 20 を代入。a の値が書き換わるので a = 20。
ポインタを使えば、変数の値を間接的に変更できます。

確認問題3 ― NULLポインタ

int *p = NULL;
printf("%d\n", *p);

このコードはどうなる?

0
NULL
実行時エラー(Segmentation Fault)
コンパイルエラー
解説: NULLポインタを間接参照(*p)すると未定義動作
多くの環境で Segmentation Fault になります。ポインタ使用前のNULLチェックは必須です。

確認問題4 ― swap関数

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;
}

出力結果は?

x=3, y=7
x=7, y=3
x=7, y=7
コンパイルエラー
解説: ポインタ経由で実体を交換。*a = x, *b = y なので、tmp=3, *a=7, *b=3。
結果: x=7, y=3。これがポインタを関数引数に使う典型的なパターンです。

確認問題5 ― ポインタと配列

int a[3] = {10, 20, 30};
int *p = a;
printf("%d\n", p[1]);

出力結果は?

10
20
30
コンパイルエラー
解説: 配列名 a は式の中では先頭要素へのポインタとして扱われます。
  • p = a で p は a[0] を指す
  • p[1]*(p + 1) と同じで、a[1] = 20
ポインタに対しても添字演算子 [] が使えるのが、ポインタと配列の密接な関係を示しています。

確認問題6 ― ポインタ演算

int a[4] = {1, 2, 3, 4};
int *p = a;
p++;
printf("%d\n", *p);

出力結果は?

1
2
3
アドレス値
解説: ポインタの ++ は「指している型のサイズ分」アドレスを進めます。
  • 初期状態: p は a[0] (=1) を指す
  • p++: p は a[1] を指すようになる(int 4バイト分進む)
  • *p = a[1] = 2
int* なら sizeof(int) 分、char* なら 1 バイト分進みます。配列を走査する際の基本テクニックです。

確認問題7 ― NULLチェック

void safe_print(int *p) {
    if (p != NULL) {
        printf("%d\n", *p);
    } else {
        printf("NULL\n");
    }
}
int main(void) {
    safe_print(NULL);
    return 0;
}

出力結果は?

0
NULL
Segmentation Fault
コンパイルエラー
解説: ポインタを扱う関数では、NULL チェックが重要です。
  • 引数 p に NULL が渡されても、if (p != NULL) でガードしているため *p は実行されない
  • else 分岐に入って "NULL" が出力される
NULL チェックなしで *p を実行すると Segmentation Fault になります。安全なコードを書くうえで必須のイディオムです。

確認問題8 ― ダングリングポインタ

int *danger(void) {
    int x = 42;
    return &x;  // ローカル変数のアドレスを返す
}
int main(void) {
    int *p = danger();
    printf("%d\n", *p);
    return 0;
}

このコードの問題は?

必ず 42 が表示される
未定義動作(ローカル変数は関数終了で無効化)
コンパイルエラー
何も問題はない
解説: 関数内のローカル変数 x は、関数が終わるとスタックから消滅します。
  • そのアドレスを返しても、呼び出し側では x の寿命は切れている
  • *p のアクセスはダングリングポインタを参照する未定義動作
  • 偶然 42 が見えることもあるが保証されない
解決策: static 変数にする、動的確保(malloc)する、引数で領域を受け取る、など。

確認問題9 ― ポインタ同士の代入

int x = 1, y = 2;
int *p = &x;
int *q = &y;
p = q;
*p = 99;
printf("x=%d, y=%d\n", x, y);

出力結果は?

x=99, y=2
x=1, y=99
x=99, y=99
x=1, y=2
解説: p = q は「p が q と同じアドレスを指すようにする」操作(値のコピーではない)。
  • p = q の後、p は y を指す(x を指すのをやめる)
  • *p = 99 で y に 99 が代入される
  • x は変更されず 1 のまま
p = q(ポインタの値の代入)と *p = *q(指している値のコピー)は全く別の操作なので注意。

結果

回答してください
ポインタの講座に戻るホームへ
← 前の講座
第34回 ポインタの基礎
次の講座 →
第36回 構造体(struct)

この講座の理解を深めるおすすめ書籍

サイトで動きを理解し、書籍で演習量を補うと効果的です

📘
苦しんで覚えるC言語
MMGames 著
初心者向けの定番入門書。
Amazonで見る
📗
新・明解C言語 入門編
柴田望洋 著
図解が豊富で演習問題も充実。
Amazonで見る
📙
プログラミング言語C 第2版
B.W.カーニハン, D.M.リッチー 著
通称K&R。C言語の原典。
Amazonで見る

※ 上記リンクはアフィリエイトリンクです。購入によりサイト運営を支援いただけます。

この記事をシェア
X(Twitter)でシェア Facebookでシェア LINEで送る はてブ