🇯🇵 日本語

STEP 6 総合復習 ― ポインタ・構造体・malloc・ファイル

第34〜42回までの要点を チートシート でおさらいし、コード読み取り6問「よくあるミス」チェックリスト で定着度を確認します。

① 要点チートシート

STEP 6 で押さえておきたい要点を、表で一気に 見直しましょう。

📍 ポインタの基本

int x = 5;
int *p = &x;     // p は x の住所を持つ
printf("%d\n", *p); // 5 (中身を取り出す)
*p = 100;          // x も 100 になる!
  • &x: x の住所(アドレス)
  • *p: p が指す先の中身(参照外し)
  • int *p* は宣言、*p = 5* は参照外し

🏢 構造体(struct)

struct Point {
    int x, y;
};

struct Point p = { 3, 4 };
printf("%d %d\n", p.x, p.y); // 3 4

struct Point *pp = &p;
printf("%d\n", pp->x);  // 3 (-> は (*pp).x の略)
  • 関連するデータを 1 つにまとめる(型を作る)
  • メンバ参照: 値なら .、ポインタなら ->

💾 malloc / free(動的メモリ)

#include <stdlib.h>

int *a = malloc(sizeof(int) * 10);
if (a == NULL) { /* 失敗 */ }

a[0] = 42;
free(a);             // 必ず解放!
a = NULL;            // 二重解放防止
  • 必要な分だけ 実行時に 確保(配列の長さも実行時に決められる)
  • 使い終わったら 必ず free(解放しないとメモリリーク)
  • 失敗時は NULL が返る

📁 ファイル入出力

FILE *fp = fopen("data.txt", "r");
if (fp == NULL) { /* 開けなかった */ }

int n;
fscanf(fp, "%d", &n);
fclose(fp);          // 必ず閉じる!
  • モード: "r"(読み) / "w"(書き、上書き) / "a"(追記)
  • fopen 後は必ず NULL チェック、終わったら fclose

⚡ ビット演算

記号意味
&AND5 & 3 → 1
|OR5 | 3 → 7
^XOR5 ^ 3 → 6
~反転~5 → -6
<<左シフト1 << 3 → 8
>>右シフト8 >> 2 → 2
  • 偶数判定: n & 1 == 0
  • 2 倍 / 1/2: n << 1 / n >> 1
  • 論理 && とビット & は別物

② コード読み取り 6問

頭の中で実行 してから選択肢をクリック。間違えても解説を読めば必ず腑に落ちます。

Q1. ポインタ経由で値を変える

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

出力は?

10
99
アドレス(不定値)
コンパイルエラー
解説: p = &x で p は x の住所を指す。
*p = 99 は「p が指す先(= x)に 99 を入れる」。
∴ x は 99 になる。
ポインタ経由で 呼出側の変数を書き換えられる のが C の強み(と落とし穴)。

Q2. ポインタで swap

void swap(int *a, int *b) {
    int t = *a;
    *a = *b;
    *b = t;
}
int x = 3, y = 7;
swap(&x, &y);
printf("%d %d\n", x, y);

出力は?

3 7(変わらない)
7 3
不定
コンパイルエラー
解説: ポインタを通して 呼出側の x, y を直接 書き換えるパターン。
① t = *a = 3
② *a = *b → x = 7
③ *b = t → y = 3
7 3
値渡しでは交換できない(ローカルでコピーが入れ替わるだけ)が、ポインタ渡しなら可能。

Q3. 構造体ポインタの -> アクセス

struct Point { int x, y; };

struct Point p = {10, 20};
struct Point *pp = &p;
pp->x = 99;
printf("%d %d\n", p.x, p.y);

出力は?

10 20(変わらない)
99 20
99 99
コンパイルエラー
解説: pp->x(*pp).x の省略形で、p のメンバ x を直接指す。
pp->x = 99p.x が 99 に なる。
99 20
-> は構造体ポインタの「中の」メンバを触る記号。

Q4. malloc + free

#include <stdlib.h>

int *a = malloc(sizeof(int) * 3);
a[0] = 10; a[1] = 20; a[2] = 30;
int sum = a[0] + a[1] + a[2];
free(a);
printf("%d\n", sum);

出力は?

60
不定(free 後だから)
0
コンパイルエラー
解説: sum は free 前に計算 済み。free しても sum の値は消えない(sum はローカル変数)。
60
※ もし free 後に a[0] を読もうとしたら未定義動作(use-after-free)。

Q5. ビット AND(偶数判定)

for (int n = 1; n <= 5; n++) {
    if ((n & 1) == 0) printf("%d ", n);
}

出力は?

1 3 5
2 4
1 2 3 4 5
何も出ない
解説: n & 1 は最下位ビットを取り出す。偶数なら 0、奇数なら 1。
(n & 1) == 0偶数のとき真
1〜5 のうち偶数は 2 と 4 → 2 4
n % 2 == 0 と同じ意味だが、ビット演算の方が高速(古典的な手法)。

Q6. 構造体のコピー

struct P { int x, y; };

struct P a = {1, 2};
struct P b = a;       // コピー
b.x = 99;
printf("%d %d %d %d\n", a.x, a.y, b.x, b.y);

出力は?

1 2 99 2
99 2 99 2
99 99 99 99
コンパイルエラー
解説: struct P b = a は構造体ごと 値コピー。a と b は独立した別物。
b.x を変えても a.x は変わらない。
1 2 99 2
※ ポインタメンバを持つ構造体だと「浅いコピー」問題が起きるが、ここでは int だけなので問題なし。

③ よくあるミス・難所チェックリスト

読みながら「やったことある…」と思ったら、その項目はもう一度元のページに戻って確認しておきましょう。
  1. 1
    未初期化のポインタを使ってクラッシュした
    int *p; *p = 5; は p が指す先が不明 → 未定義動作。宣言時に NULL か有効なアドレスで初期化。
  2. 2
    malloc した後 free を忘れた
    プログラムが長く動くと メモリリーク で動作が重くなる/クラッシュ。確保したら必ず解放
  3. 3
    free したポインタを使った(use-after-free)
    解放済みメモリにアクセス → 未定義動作。free 後は p = NULL にしておくと安全。
  4. 4
    fopen の結果を NULL チェックせず使った
    ファイルが無いと NULL が返る。NULL に対して fscanf すると クラッシュ。常に if(fp == NULL) で確認。
  5. 5
    fclose を忘れた
    書き込み内容がフラッシュされないままになるなど、データ損失の原因。読み終わったら必ず閉じる。
  6. 6
    構造体ポインタで .-> を取り違えた
    値なら p.x、ポインタなら pp->xpp.x はコンパイルエラー。
  7. 7
    論理 && とビット & を混同した
    (a == 1) & (b == 2) は両辺の真偽をビット AND するので一見動くが、短絡評価が効かず副作用が変わる。条件は &&

④ 戻るページ ― つまずいたらここに戻る

特定の項目で迷ったら、対応する元ページにすぐ戻れるよう一覧にしました。
第34回 ポインタの基礎&* の意味が曖昧な時に 第35回 確認問題(ポインタ)類題で別アングルから理解を確認 第36回 構造体(struct)メンバアクセス(. と ->)の確認に 第38回 共用体(union)メモリを共有する仕組みを再確認 第39回 ファイル入出力fopen/fclose・モード指定の確認に 第40回 動的メモリmalloc/free・NULL チェックの確認に 第42回 ビット演算シフト・AND/OR/XOR の動きを確認 チートシート(早見表)ポインタ・構造体・malloc を1ページで確認