C言語学習ポータル(無料版) 学習コンテンツ 文字列 安全な文字列操作——バッファサイズの意識

🔤 文字列

安全な文字列操作——バッファサイズの意識

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

🎯 バッファオーバーフローの危険を理解し、snprintfやfgetsで安全に書ける

文字列を扱う配列のことを、バッファ(buffer)と呼びます。そしてCの歴史上、最も多くの被害を出してきたバグが、このバッファから溢れる事故です。

char name[5];
strcpy(name, "Alexander");    // 9文字+'\0' で10バイト必要なのに、箱は5つ

コンパイルは通ります。実行すると、strcpy は行き先の大きさを知らないまま10バイトを書き込み、配列の外へはみ出します。隣の変数が壊れる、関数の戻り先が壊れる、あるいは何事もなく動いてしまう——境界外アクセスそのものです。

これがバッファオーバーフローで、攻撃者が意図的に長いデータを送り込んでプログラムを乗っ取る手口としても知られています。

書き込む前に、入る大きさを渡す

対策の原則はひとつ。行き先の大きさを関数に教えることです。いちばん使いやすいのが snprintf です。

#include <stdio.h>

int main(void)
{
    char name[5];

    snprintf(name, sizeof(name), "%s", "Alexander");
    printf("%s\n", name);       // Alex(4文字+'\0'。溢れずに切り詰められる)
    return 0;
}

snprintf(行き先, 行き先の大きさ, 書式, …) の形です。指定した大きさを超えることはなく、末尾には必ず '\0' が付きます。書式指定子が使えるので、数値と文字列を混ぜて組み立てることもできます。

strncpy の落とし穴

「n付き」の strncpy もありますが、こちらは注意が必要です。コピー元が長いと、'\0' が付かないまま打ち切られます。

char dst[5];
strncpy(dst, "Alexander", sizeof(dst));   // "Alex" までコピー、'\0' なし!
dst[sizeof(dst) - 1] = '\0';              // 自分で終端する必要がある

安全そうな名前なのに一手間が要る、というのがCらしいところです。迷ったら snprintf を選ぶのが無難です。

入力も同じ考え方

キーボードからの入力にも同じ危険があります。scanf の %s は入力の長さを制限しないため、長い入力で簡単に溢れます。文字列の入力には fgets を使いましょう。

#include <stdio.h>

int main(void)
{
    char line[64];

    if (fgets(line, sizeof(line), stdin) != NULL) {
        printf("入力: %s", line);
    }
    return 0;
}

fgets(行き先, 大きさ, stdin) も大きさを渡す形です。なお fgets は改行文字も一緒に取り込むので、必要なら取り除きます(t09-08で扱います)。かつて存在した gets という関数は、大きさを指定できず危険すぎたため、標準から削除されました。

罠・注意

師範器の大きさを知らぬ者に、水を注がせてはならん

まとめ: 文字列を書き込む関数には、必ず行き先の大きさを渡す。strcpy より snprintf、scanf("%s") より fgets。

要点まとめ

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

この項目に出てくる用語

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

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

文字列もじれつ

文字が連なったデータのこと。C言語ではchar型の配列として表現され、その末尾には終わりを示す番人の文字が置かれる。"Hello"のようにダブルクォートで囲んで書くのが文字列リテラル。

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

文字列とはchar配列であるCの文字列がchar配列であることを理解し、宣言・表示・1文字ずつのアクセスができる ヌル終端 '\0' の掟ヌル終端の役割を理解し、必要な配列サイズを正しく見積もれる 文字列リテラルと初期化文字列の初期化の書き方を使い分け、代入できない理由を説明できる string.h の主要関数strlen・strcpy・strcat・strcmp の役割と使い方を説明できる 文字の判定と変換(ctype.h)ctype.hの判定・変換関数を使って、文字の種類を調べたり大文字小文字を変換できる 文字コードの基礎——ASCIIとUTF-8文字が数値であることを理解し、文字コードを使った計算ができる 文字列処理の定石パターン文字列を1文字ずつ走査する型を身につけ、数える・削る・逆順にするが書ける
‹ string.h の主要関数 文字の判定と変換(ctype.h) ›

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

完全版(BOOTH)を見る

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

Web版 / BOOTH