web-dev-qa-db-ja.com

snprintf()は常にnullで終了しますか?

Snprintfは常にnullで宛先バッファを終了しますか?

つまり、これで十分ですか?

char dst[10];

snprintf(dst, sizeof (dst), "blah %s", somestr);

または、somestrが十分に長い場合、このようにする必要がありますか?

char dst[10];

somestr[sizeof (dst) - 1] = '\0';
snprintf(dst, sizeof (dst) - 1, "blah %s", somestr);

私は、標準が何を言っているのか、標準的な動作ではない人気のあるlibcが何をするのかについて興味があります。

71
Prof. Falken

他の答えが確立するとき:それ should

snprintf ...結果を文字列バッファに書き込みます。 (...)は、buf_sizeがゼロでない限り、ヌル文字で終了します。

そのため、(明らかに)ゼロを "nowhere"に書き込むことができないため、ゼロサイズのバッファを渡さないように注意する必要があります。


ただし、Microsoftのライブラリに注意してください 持っていないsnprintfという関数が、代わりに 歴史的にonlyには_snprintf(先頭のアンダースコアに注意)と呼ばれる関数がありました。これは追加しない終端のnullです。ドキュメントは次のとおりです(VS 2012、~~ VS 2013):

http://msdn.Microsoft.com/en-us/library/2ts7cx93%28v=vs.110%29.aspx

戻り値

Lenをフォーマットされたデータ文字列の長さとします(終端のnullは含みません)。 lenとcountは、_snprintfの場合はバイト単位、_snwprintfの場合はワイド文字です。

  • Len <countの場合、len文字がバッファに格納され、null終了文字が追加され、lenが返されます。

  • Len = countの場合、len文字がバッファに格納され、nullターミネータは追加されず、lenが返されます。

  • Len> countの場合、count文字はバッファーに格納され、nullターミネーターは追加されず、負の値が返されます。

(...)

Visual Studio 2015 (VC14)は明らかにsnprintf関数に準拠していますが、先行アンダースコアとnon null終了動作を備えたレガシー関数はまだ存在しています:

snprintf関数は、buffer[count-1]にヌルターミネータを配置することにより、lenがcount以上の場合に出力を切り捨てます。 (...)

すべての関数other than snprintfの場合、len = countの場合、len文字はバッファに格納されます。no null-terminatorは追加されません、(...)

64
Martin Ba

Snprintf(3)マンページによると。

関数snprintf()およびvsnprintf()は、sizeに最大strバイト(末尾のヌルバイト( '\ 0')を含む)を書き込みます。

そのため、サイズが1以上の場合、終了する必要はありません。

17
piotr

C規格によれば、バッファサイズが0でない限り、 vsnprintf() および snprintf() nullは出力を終了します。

snprintf()関数はsprintf()と同等であり、sによって参照されるバッファのサイズを示すn引数が追加されます。 nがゼロの場合、何も書き込まれず、sはNULLポインターになる場合があります。そうでない場合、n-1番目を超える出力バイトは配列に書き込まれる代わりに破棄され、実際に配列に書き込まれたバイトの最後にnullバイトが書き込まれます。

したがって、割り当てるバッファの大きさを知る必要がある場合は、サイズ0を使用します。その後、宛先としてNULLポインターを使用できます。 POSIXページにリンクしていることに注意してください。ただし、これらは、同じ基準をカバーするStandard CとPOSIXの間に相違があることを意図していないことを明確に述べています。

このリファレンスページで説明されている機能は、ISO C標準に準拠しています。ここで説明する要件とISO C規格との間に矛盾がある場合、それは意図的なものではありません。このPOSIX.1-2008のボリュームは、ISO C標準に従います。

Microsoftバージョンの vsnprintf() に注意してください。バッファに十分なスペースがない場合、標準Cバージョンとは確実に異なる動作をします(標準関数が必要な長さを返す場合は-1を返します)。 Microsoftバージョンがエラー状態でその出力をnullで終了するのは完全には明らかではありませんが、標準のCバージョンは終了します。

TR 24731セーフ関数を使用しますか? (Microsoftバージョンのvsprintf_s())および 安全でないC標準ライブラリ関数の安全な代替手段のMacソリューション?

10

一部の古いバージョンのSunOSはsnprintfで奇妙なことを行い、出力をNULで終了せず、戻り値が他の誰もしていたものと一致しなかったかもしれませんが、過去10年間にリリースされたものはすべてC99言う。

4
Art

あいまいさは、C標準自体から始まります。 C99とC11の両方には、snprintf関数の同じ説明があります。 C99の説明を次に示します。

7.19.6.5 snprintf関数
概要
1 #include <stdio.h> int snprintf(char * restrict s, size_t n, const char * restrict format, ...);
説明
2 snprintf関数は、出力がストリームではなく配列(引数fprintfで指定)に書き込まれることを除いて、sと同等です。 nがゼロの場合、何も書き込まれず、sはNULLポインターになる場合があります。そうでない場合、n-1stを超える出力文字は配列に書き込まれるのではなく破棄され、実際に配列に書き込まれる文字の最後にヌル文字が書き込まれます。重複するオブジェクト間でコピーが行われる場合、動作は未定義です。
返品
3 snprintf関数は、nが十分に大きく、終端のヌル文字をカウントしない場合に書き込まれた文字数、またはエンコードエラーが発生した場合は負の値を返します。したがって、戻り値が負でなくnより小さい場合にのみ、ヌル終了出力が完全に書き込まれます。

一方で

それ以外の場合、n-1stを超える出力文字は破棄されます配列に書き込まれるのではなく、および実際に書き込まれた文字の最後にヌル文字が書き込まれます配列

と言う
if(sは3文字の長さの配列を指し、nが3の場合、2文字が書き込まれ、2番目の文字を超える文字が書き込まれます破棄されます;次に、ヌル文字はそれらの2の後に書き込まれます(そしてヌル文字は書き込まれる3番目の文字になります)

そして、これは元の質問に答えると信じています。
回答:
重複するオブジェクト間でコピーが行われる場合、動作は未定義です。
nが0の場合、出力には何も書き込まれません
それ以外の場合、エンコードエラーが発生しなかった場合、出力は常にヌルで終了します出力が出力配列に収まるかどうかに関係なくor not;そうでない場合、出力配列がオーバーフローしないようにいくつかの文字が破棄されます)、
それ以外の場合(エンコードエラーが発生した場合)、出力はnullで終了しません

一方で
最後の文

したがって、戻り値が負でなくnより小さい場合にのみ、ヌル終了出力が完全に書き込まれます。

ambiguity(または私の英語が十分ではありません)を与えます。この文は少なくとも2つの方法で解釈できます。
1。出力はnull-terminatedで、戻り値が非負で、かつnより小さいの場合のみ(戻り値がnot n未満、つまり、出力(終端のヌル文字を含む)が配列に収まらない場合、出力はヌル終端されません)。
2。出力はcomplete(文字が破棄されていない)であり、戻り値が非負でn未満の場合のみです。


上記の解釈1は答えと矛盾し、誤解と長時間の議論を引き起こすと信じています。そのため、snprintf関数を説明する最後の文は、あいまいさをなくすために変更が必要です(C言語標準への提案を書く根拠を与えます)。
曖昧でない表現の例は、 http://en.cppreference.com/w/c/io/fprintf4)を参照) 、リンクの@ "Martin Ba"に感謝します。

質問「 snprintf:このfuncの説明を変更するC標準提案/計画はありますか? 」も参照してください。

4
Robin Kuzmin