呼び出しの%s
変換指定子は、対応する引数が、入力を保持するのに十分な大きさのタイプの書き込み可能なバッファーscanf
を指すことを期待しています。char [N]
N
line
文字列リテラルを指すように初期化しました""
。これには 2 つの問題があります。1 つ目は、文字列リテラルの内容を変更しようとすると、未定義の動作が発生することです。言語定義では、文字列リテラルの格納方法は指定されていません。それらの有効期間と可視性のみを指定し、一部のプラットフォームはそれらを読み取り専用メモリ セグメントに貼り付けますが、他のプラットフォームは書き込み可能なデータ セグメントに配置します。したがって、あるプラットフォームで文字列リテラルの内容を変更しようとすると、アクセス違反が原因で完全にクラッシュする可能性がありますが、別のプラットフォームでは同じことが正常に機能する場合があります。言語定義は、何をすべきかを義務付けていません文字列リテラルを変更しようとすると発生します。実際、その動作を明示的にundefinedのままにするため、コンパイラは状況を自由に処理できます。一般に、文字列リテラルは書き込み不可であると常に想定することをお勧めします。
もう 1 つの問題は、文字列リテラルを含む配列のサイズが 0 ターミネータの 1 文字しか保持できないことです。C スタイルの文字列は の単純な配列として格納されchar
、さらに文字を追加しても配列は自動的に拡張されないことに注意してください。
line
の配列として宣言するかchar
、メモリを動的に割り当てる必要があります。
char line[MAX_INPUT_LEN];
また
char *line = malloc(INITIAL_INPUT_LEN);
メモリを動的に割り当てる利点は、必要に応じてバッファのサイズを変更できることです。
安全のために、読み取る最大文字数を指定する必要があります。バッファーのサイズが 21 文字を保持するサイズの場合、scanf
呼び出しを次のように記述します。
scanf("%20s", line);
line
保持できるよりも多くの文字が入力ストリームにある場合、scanf
はそれらの余分な文字を に続くメモリに書き込みline
、重要なものを破壊する可能性があります。バッファ オーバーフローは一般的なマルウェアのエクスプロイトであり、回避する必要があります。
また、%s
完全な行は得られません。フィールド幅指定子があっても、次の空白文字まで読み取ります。のような別の変換指定子を使用する%[^\n]
か、fgets()
代わりに使用する必要があります。