C言語学習ポータル(無料版) 学習コンテンツ 配列 境界外アクセスという重大事故

🗃️ 配列

境界外アクセスという重大事故

無料公開版に収録・読む目安 14分

🎯 境界外アクセスが未定義動作であることを理解し、危険性と防ぎ方を説明できる

このトピックは、C言語を学ぶうえで最も重要な安全教育です。文法よりも先に、体に刻んでください。

int a[3] と宣言したのに a[3] や a[5] に触れる。これを境界外アクセスと呼びます。そしてCは、それを黙って通します。

#include <stdio.h>

int main(void)
{
    int a[3] = {10, 20, 30};
    int x = 99;

    a[3] = 777;                  // 配列の外!
    printf("x = %d\n", x);       // x = 777 になることがある
    return 0;
}

x は一度も書き換えていないのに、値が変わることがあります。配列のすぐ隣に置かれていた x の場所を、a[3] が踏んだからです。下のビジュアライザで、その瞬間を見てください。

🧪 ここはメモリの中身を1ステップずつ追う「メモリビジュアライザ」の場面です(アプリ本体で動きます)。

なぜCは止めてくれないのか

多くの言語は、範囲外の添字を検出してエラーで止めてくれます。Cがそうしないのは、チェックの費用を払わないという設計思想だからです。要素にアクセスするたびに「範囲内か?」と確認すれば、その分だけ必ず遅くなります。Cは「プログラマが正しく書く」ことを前提に、その確認を省いて速度を取りました。OSや組込み機器がCで書かれてきた理由でもあり、同時に、事故がプログラマの責任になる理由でもあります。

いちばん怖いのは「すぐ落ちない」こと

境界外アクセスは未定義動作です。何が起きるかは決まっていません。

「動いたから正しい」がまったく通用しないのが、この事故の恐ろしさです。数か月後、まったく別の場所を修正した日に突然表面化します。しかも壊れるのは「はみ出した側」ではなく「踏まれた側」なので、調査は無関係な変数から始まります。原因にたどり着くまでの遠回りが、この事故の本当のコストです。

セキュリティの入口でもある

配列の外へ書き込める状態は、攻撃者から見れば「プログラムの記憶を書き換えられる穴」です。歴史上の深刻な脆弱性の多くが、このバッファオーバーフローに起因しています。第6部で正面から扱いますが、いまの段階では「配列の外に書くのは、鍵をかけ忘れた玄関と同じ」と覚えておいてください。

防ぎ方

  1. ループ条件は必ず i < n。<= を書いたら一度立ち止まる
  2. 要素数は #define などで1か所にまとめ、配列とループで同じ名前を使う
  3. 添字を計算で作るときは、その式が取りうる最小値と最大値を必ず確かめる
  4. 外から来た数値(入力・計算結果)を添字にする前に、if (i >= 0 && i < n) で確認する
  5. コンパイラの警告を必ず有効にする(多くの単純なはみ出しは検出できます)

とくに4番目が実戦では効きます。添字が変数で決まるときは、触る前に範囲を確かめる。それだけで多くの事故は防げます。

#include <stdio.h>

#define N 3

int main(void)
{
    int a[N] = {10, 20, 30};
    int i = 3;                     // 外から来た値のつもり

    if (i >= 0 && i < N) {         // 触る前に確認する
        printf("%d\n", a[i]);
    } else {
        printf("範囲外です\n");
    }
    return 0;
}
師範Cは止めてくれん。止めるのは、書き手の作法じゃ

まとめ: 境界外アクセスは未定義動作。落ちないことすらある。だからこそ「範囲は 0 〜 n−1」を、書くたびに確認する習慣が最大の防御になります。

要点まとめ

アプリで演習問題まで解く(無料・登録不要)

この項目に出てくる用語

境界外アクセスきょうかいがいあくせす

配列の要素数を超えた添字にアクセスしてしまう重大な事故のこと。コンパイルは通ってしまうため、実行時に予期せぬ動作を引き起こす。他の変数の領域を書き換えてしまい、原因の分かりにくいバグにつながりやすい。

配列はいれつ

同じ型の値を一列に並べて、まとめて管理するデータ構造のこと。1つの名前で複数の値をまとめて扱える、箱の行列のようなもの。int arr[5];のように、要素の型と個数を指定して宣言する。

同じトラックのほかの項目

配列とは——箱の行列配列の宣言と要素へのアクセスができ、変数を並べるより配列が優れる理由を説明できる 添字と0始まりの理由添字が0始まりである理由を理解し、範囲を間違えずにループを書ける 配列とループの黄金コンビforループで配列全体を走査でき、要素数を安全に管理できる 配列の初期化いろいろ配列の初期化の書き方を使い分けられ、未初期化の危険を避けられる 多次元配列2次元配列を宣言・初期化し、二重ループで表として扱える 配列を関数に渡す——入口編配列を関数へ渡す書き方を身につけ、値渡しとの違いに気づける 集計・最大値・検索の定石合計・最大値・線形探索の定型パターンを自力で書ける
‹ 配列とループの黄金コンビ 配列の初期化いろいろ ›

もっと先へ:ポインタ・メモリ・ファイル入出力・セキュアコーディングを含む全26トラックと、段位検定・模試のフルセットは完全版に収録しています。

完全版(BOOTH)を見る

📚 姉妹教材:手を動かして覚える Linux 教材 — Linuxとインフラの仕組みを地図で学ぶ

Web版 / BOOTH