C言語でプログラミングを学ぶ際、標準入力から文字列を取得するために「gets関数」を教科書や古い資料で見かけることがあるかもしれません。
しかし、現代のシステム開発において、gets関数は単なる「古い関数」ではなく、プログラムの安全性を根底から揺るがす深刻な脆弱性の原因として扱われています。
2026年現在のC言語標準規格においても、gets関数の使用は完全に否定されており、代替手段への移行が必須の知識となっています。
この記事では、なぜgets関数が危険なのかという技術的な理由から、標準的な代替案であるfgets関数への具体的な書き換え手順まで、プロフェッショナルが知っておくべき詳細な情報を解説します。
C言語におけるgets関数の基礎知識と背景
gets関数は、標準入力(stdin)から改行文字(’\n’)またはファイル終端(EOF)に達するまで文字列を読み込み、指定されたバッファに格納する関数です。
この関数は、C言語の黎明期から標準ライブラリ(stdio.h)の一部として存在し、直感的に文字列を取得できるため、多くのプログラミング入門書で使用されてきました。
gets関数の基本的な構文
gets関数のプロトタイプ宣言は以下のようになっています。
// gets関数の定義例
char *gets(char *s);
この関数は、読み込んだ文字列を格納するための配列の先頭ポインタのみを引数として受け取ります。
戻り値としては、成功した場合には引数で渡されたポインタを返し、エラーが発生した場合や入力が空のままEOFに達した場合にはNULLを返します。
歴史的な位置づけと規格からの削除
gets関数は、その設計上の問題から、ISO/IEC 9899(C言語の標準規格)において段階的に廃止されてきました。
1999年に策定されたC99規格では、すでにgets関数は非推奨(deprecated)とされていました。
その後、2011年のC11規格において、標準ライブラリから完全に削除されるという異例の措置が取られました。
現代のC17やC23規格、そして2026年現在の主要なコンパイラ環境では、gets関数を呼び出すだけで警告やエラーが出力されるのが一般的です。
なぜgets関数は「危険」と言われるのか
gets関数が危険視される最大の理由は、「入力されるデータのサイズを制限する手段がない」という設計ミスにあります。
プログラムが用意したメモリ領域(バッファ)のサイズを超えてデータが書き込まれてしまう現象を、バッファオーバーフローと呼びます。
バッファオーバーフローのメカニズム
例えば、10文字分の配列を用意して、そこにgets関数でユーザーからの入力を受け取るとします。
#include <stdio.h>
int main(void) {
char buf[10];
printf("名前を入力してください: ");
gets(buf); // ここが極めて危険
printf("あなたの名前は %s です。\n", buf);
return 0;
}
もしユーザーが100文字の文字列を入力した場合、gets関数は配列の境界を無視して、隣接するメモリ領域を破壊しながらデータを書き込み続けます。
これにより、他の変数の値が書き換えられたり、プログラムの戻り先アドレスが改ざんされたりします。
セキュリティ攻撃への悪用
この脆弱性は、古くからサイバー攻撃の標的となってきました。
1988年に発生し、インターネット全体に甚大な被害を与えた「モリスワーム」は、このgets関数の脆弱性を悪用したことで知られています。
攻撃者は意図的に長い文字列を入力することで、プログラムの実行権限を乗っ取り、任意の悪意あるコードを実行させることが可能になります。
現代においても、gets関数を使用し続けることは、システムに巨大な穴を開けたまま放置するのと同じことなのです。
gets関数とfgets関数の比較
gets関数の唯一の代替として最も推奨されるのが、fgets関数です。
fgets関数は、入力される最大サイズを引数として指定できるため、バッファオーバーフローを確実に防止できます。
両者の主な違い
以下の表は、gets関数とfgets関数の主要な違いをまとめたものです。
| 特徴 | gets関数 | fgets関数 |
|---|---|---|
| サイズ指定 | 不可能 | 可能(安全) |
| 改行文字(\n)の扱い | 削除される | バッファに格納される |
| 標準規格の現状 | 削除済み(使用不可) | 標準(推奨) |
| 入力元 | 標準入力のみ | ファイルポインタで指定可能 |
fgets関数は、指定されたサイズから1を引いた数(終端文字\0の分を考慮)までしか読み込まないため、安全性が担保されています。
安全なfgets関数への書き換え手順
既存のコードにあるgets関数をfgets関数に置き換える際には、単に関数名を変えるだけでは不十分です。
fgets関数の挙動を正しく理解し、適切な処理を追加する必要があります。
基本的な書き換えパターン
まず、単純な置き換えの例を見てみましょう。
// 修正前:危険なコード
gets(buf);
// 修正後:安全なコード
fgets(buf, sizeof(buf), stdin);
第一引数にはバッファのポインタ、第二引数にはバッファ全体のサイズ、第三引数には標準入力を示すstdinを指定します。
改行文字の除去処理
fgets関数は、入力された改行文字(’\n’)をそのままバッファに含めます。
gets関数と同じ挙動(改行文字を含まない状態)にするには、手動で改行文字を取り除く必要があります。
#include <stdio.h>
#include <string.h>
int main(void) {
char buf[64];
printf("文字列を入力してください: ");
if (fgets(buf, sizeof(buf), stdin) != NULL) {
// 改行文字を検索して、見つかったら終端文字に置き換える
char *p = strchr(buf, '\n');
if (p != NULL) {
*p = '\0';
}
}
printf("入力された内容: [%s]\n", buf);
return 0;
}
この処理を加えることで、バッファオーバーフローを防ぎつつ、従来のgets関数と同様の文字列操作が可能になります。
入力バッファのフラッシュ(残存データの処理)
fgets関数で指定したサイズ以上のデータが入力された場合、読みきれなかったデータは標準入力のバッファに残ってしまいます。
これが残ると、次回の入力処理で意図しない挙動を引き起こす原因となります。
必要に応じて、以下のようにバッファをクリアする処理を検討してください。
// 改行が見つからない=入力がバッファサイズを超えた場合の処理
if (strchr(buf, '\n') == NULL) {
int c;
while ((c = getchar()) != '\n' && c != EOF);
}
2026年における最新の入力処理のベストプラクティス
現代のC言語プログラミングでは、より堅牢なプログラムを作成するために、単純なfgetsの利用以上の工夫が求められます。
特に、商用ソフトウェアやセキュリティが重視される環境では、以下のポイントを意識することが重要です。
動的メモリ確保の活用
固定長の配列では、非常に長い入力に対応できない場合があります。
入力サイズをあらかじめ制限できない場合は、realloc関数を使用して、入力に応じて動的にメモリを拡張する自作関数を作成する手法がとられます。
これにより、メモリ効率と安全性を両立させることができます。
OS依存の安全な関数(gets_sなど)
C11規格のAnnex K(付属書K)では、gets_sという関数が定義されています。
これはgets関数にサイズ指定を追加したもので、一部のコンパイラ(Microsoft Visual C++など)でサポートされています。
// 一部の環境で利用可能なgets_s
gets_s(buf, sizeof(buf));
ただし、Annex Kはすべてのコンパイラで実装されているわけではないため、移植性を重視する場合はfgetsを使用するのが2026年時点でも最も確実な選択です。
エラーハンドリングの徹底
入力処理は常に外部からの不確実なデータにさらされます。
戻り値がNULLでないか、フェイルセーフな設計になっているかを常にチェックする癖をつけましょう。
「ユーザーは常に開発者の想定外の入力をする」という前提に立つことが、バグのないプログラミングへの第一歩です。
まとめ
C言語のgets関数は、バッファオーバーフローという致命的な脆弱性を誘発するため、現代のプログラミングにおいては完全に排除されるべき存在です。
かつての教科書に載っていたからといって、現在の開発現場や学習環境で利用することは、プログラムの品質を著しく低下させ、セキュリティリスクを高める行為に他なりません。
安全な代替手段であるfgets関数を正しく理解し、改行文字の扱いやバッファサイズ管理を適切に行うことが、現代のC言語プログラマにとって必須のスキルです。
この記事で紹介した書き換え手順や注意点を参考に、常に安全性を意識した堅牢なコードを記述することを心がけてください。
gets関数を忘れ、fgets関数やより高度な入力処理手法をマスターすることが、プロフェッショナルなエンジニアへの道に繋がります。
