43

標準ライブラリの多くの文字列関数にバッファが提供されるさまざまなケースで、バッファが null ターミネータを超えて変更されないことが保証されていますか? 例えば:

char buffer[17] = "abcdefghijklmnop";
sscanf("123", "%16s", buffer);

buffer現在等しい必要があり"123\0efghijklmnop"ますか?

もう一つの例:

char buffer[10];
fgets(buffer, 10, fp);

読み取り行の長さが 3 文字しかない場合、6 番目の文字が fgets が呼び出される前と同じであると確信できますか?

4

7 に答える 7

31

C99 ドラフト標準では、そのような場合に何が起こるべきかを明示的に述べていませんが、複数のバリエーションを検討することで、すべての場合に仕様を満たすように特定の方法で動作する必要があることを示すことができます。

標準は次のように述べています。

%s - 一連の非空白文字に一致します.252)

l 長さ修飾子が存在しない場合、対応する引数は、シーケンスを受け入れるのに十分な大きさの文字配列の最初の要素へのポインターと、自動的に追加される終端の null 文字になります。

標準を満たすために提案している方法で機能する必要があることを示す例を次に示します。

例 A:

char buffer[4] = "abcd";
char buffer2[10];  // Note the this could be placed at what would be buffer+4
sscanf("123 4", "%s %s", buffer, buffer2);
// Result is buffer =  "123\0"
//           buffer2 = "4\0"

例 B:

char buffer[17] = "abcdefghijklmnop";
char* buffer2 = &buffer[4];
sscanf("123 4", "%s %s", buffer, buffer2);
// Result is buffer = "123\04\0"

sscanf のインターフェースは、これらが異なっていることを実際に知るのに十分な情報を提供しないことに注意してください。したがって、例 B が適切に機能するためには、例 A のヌル文字の後のバイトをいじってはなりません。これは、このビットの仕様に従って両方のケースで機能する必要があるためです。

したがって、暗黙のうちに、仕様のためにあなたが述べたように機能する必要があります。

他の関数にも同様の引数を配置できますが、この例からアイデアを確認できると思います。

注: 「%16s」などの形式でサイズ制限を指定すると、動作変わる可能性があります。仕様では、データをバッファーに書き込む前に、sscanf がバッファーをその制限までゼロにすることは機能的に許容されます。実際には、ほとんどの実装はパフォーマンスを選択します。つまり、残りはそのままにしておきます。

仕様の意図がこの種のゼロ化を行うことである場合、通常は明示的に指定されます。strncpy は一例です。文字列の長さが指定された最大バッファー長より短い場合、残りのスペースは null 文字で埋められます。この同じ「文字列」関数が終了していない文字列を返す可能性があるという事実により、これは人々が独自のバージョンを展開する最も一般的な関数の 1 つになります。

fgets に関する限り、同様の状況が発生する可能性があります。唯一の落とし穴は、何も読み込まれない場合、バッファはそのままであると仕様が明示的に述べていることです。許容可能な機能実装では、バッファーをゼロにする前に読み取るバイトが少なくとも 1 バイトあるかどうかを確認することで、これを回避できます。

于 2015-02-25T07:06:52.347 に答える
8

これについての標準はややあいまいですが、それを合理的に読むと、答えは次のようになると思います。はい、読み取り + null よりも多くのバイトをバッファーに書き込むことは許可されていません。一方、テキストをより厳密に読んだり解釈したりすると、答えはノーであり、保証はないと結論付けられる可能性があります。以下は、公開されているドラフトの内容ですfgets

char *fgets(char * restrict s, int n, FILE * restrict stream);

この関数は、 が指すストリームから が指す配列に、fgetsが指定する文字数より最大で 1 つ少ない文字を読み取ります。改行文字 (保持されます) の後またはファイルの終わりの後、追加の文字は読み取られません。ヌル文字は、配列に読み込まれた最後の文字の直後に書き込まれます。nstreams

成功した場合、fgets関数は戻りますs。ファイルの終わりが検出され、文字が配列に読み取られていない場合、配列の内容は変更されず、NULL ポインターが返されます。操作中に読み取りエラーが発生した場合、配列の内容は不確定であり、NULL ポインターが返されます。

入力から読み取る量についての保証があります。つまり、改行または EOF で読み取りを停止し、n-1バイトを超えて読み取らないようにします。バッファへの書き込みが許可されている量については何も明示的に述べられていませんが、一般的な知識は、fgetsnパラメータがバッファ オーバーフローを防ぐために使用されるということです。標準であいまいな用語readが使用されているのは少し奇妙です。これは、使用する用語を詳しく調べたい場合、バイトを超えてバッファに書き込めgetsないことを必ずしも意味しない場合があります。ただし、両方の問題について同じ「読み取り」用語が使用されていることに注意してください: -limit と EOF/改行制限。だから解釈するとnnn-関連の「読み取り」をバッファ書き込み制限として使用する場合、[一貫性のために] 他の「読み取り」を同じように解釈できます/解釈する必要があります。

一方、句動詞「read into」(=「write」) と単に「read」の使用を区別すると、委員会のテキストを同じように読むことはできません。バイトを超えて配列を「読み込む」(="write to") ことはないことが保証されていますnが、入力文字列が改行または EOF によってすぐに終了した場合は、残りの (入力の) 勝利のみが保証されます「読み取り」ではありませんが、それが「読み取り」(="書き込み先") ではないことを意味するかどうかは、このより厳密な読み取りではバッファーが不明です。重要な問題は、キーワードが「into」であり、省略されていることです。したがって、問題は、次の変更された引用の括弧内に私が指定した補完が意図した解釈であるかどうかです。

改行文字 (保持される) の後またはファイルの終わりの後に、追加の文字が [配列に] 読み込まれることはありません。

率直に言って、式として記述された単一の事後条件(この場合はかなり短い) は、私が引用した言葉遣いよりもはるかに役に立ちます...

家族に関する彼らの記事を分析しようとする気にはなりません*scanf。なぜなら、これらの機能で起こる他のすべてのことを考えると、さらに複雑になるのではないかと思うからです。彼らの記事fscanfは約5ページの長さです...しかし、同様の論理が当てはまると思います.

于 2015-02-25T09:38:59.037 に答える