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


85

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

言い換えれば、これで十分ですか?

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

私は、標準が何を言っているのか、そして標準的な振る舞いではないいくつかの人気のあるlibcが何をするのかということに興味があります。


2番目の例でsomestrまたはdstをヌル終了することを意味しますか?
ハドソン

@ chux、Martin Baは、受け入れられた回答でそれをカバーしました。:)
ファルケン教授2014年

@chux良かったと思いますが、あなたのコメントは、dest i 0が長い場合、何も書かれていないことを非常に明確にしました。私はすべてのコメントを、他のスタックオーバーフラワーとチャットするための潜在的な言い訳としてとらえています。:)
ファルケン教授

@Prof。Falkenは、コメントは大丈夫で明確であることに同意しますが、それは答えと重複していました-私のレビューではそれを見逃しました。
chux -復活モニカ

stackoverflow.com/a/8712996/193892 Visual Studioがsnprintf()を
Falken教授

回答:


73

他の答えが確立するように:それはすべきです:

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

したがって、注意しなければならないのは、ゼロサイズのバッファを渡さないことだけです。これは、(明らかに)「どこにも」ゼロを書き込むことができないためです。


しかし、注意してくださいMicrosoftのライブラリがあることがないという関数をsnprintf代わりに歴史的にのみ呼び出される関数ました_snprintf(注有力アンダースコア)追加しません終端ヌルを。ドキュメントは次のとおりです(VS 2012、~~ VS 2013):

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

戻り値

lenをフォーマットされたデータ文字列の長さとします(終了ヌルを含まない)。lenとcountは、_snprintfの場合はバイト単位で、_snwprintfの場合はワイド文字です。

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

  • len = countの場合、len文字はバッファーに保管され、ヌルターミネーターは追加されず、lenが返されます。

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

(...)

Visual Studioの2015(VC14)明らかに準拠した導入snprintf機能を、が、先行アンダースコアとレガシーの1ヌル終端動作がまだそこにあります。

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

すべての機能のための他のよりsnprintfLEN =カウント場合、LEN文字は、バッファに格納されないヌルターミネータが付加されていません(...)


24
アスランの名において、マイクロソフトのエンジニアは、の主要な安全機能_snprintfを静かに削除しsnprintf、文字列がnullで終了しないようにすることを導入したときに何を考えていましたか?!
Colin D Bennett

2
@ ColinDBennett-それは奇妙で非常に迷惑であり、誰かが考えたとしても私には手がかりがありません:-)
Martin Ba

2
@MartinBaええ、申し訳ありませんが、私がテストしたのはtemplate <size_t size> int _snprintf_s(char (&buffer)[size], size_t count, const char *format [, argument] ...);/ GS(セキュリティチェック)コンパイルフラグでのみ発生することにも言及する必要があります。その関数は、サイズ、数、および長さを認識しています。
sekmet64 2014年

3
mingw64は、特に指定がない限り、Microsoft _snprintf実装を「通常の」snprintfとして使用(使用?)することに注意してください。nvd.nist.gov/ vuln
detail /

2
@Sajjonそれは確かにばかげた(そしておそらく完全にオリジナルの)憤慨の叫びidioms.thefreedictionary.com/in+the+name+of+God)であり、おそらくわずかに婉曲な誓い(en.wikipedia.org/wiki/Minced_oath)です。別の例としては、「ゼウスの名前で何が...?!」などがあります。(forum.wordreference.com/threads/in-the-name-of-zeus.2132965
コリンDベネット

19

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

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

したがって、はい、サイズが1より大きい場合は終了する必要はありません。


3
そして、それを神に感謝します。これが唯一の賢明なデザインです。これらの関数のチェックされたバージョンの要点は安全であることであり、すべての終了マラキーを手作業で行わなければならない場合はひどいことになるでしょう。
Kerrek SB 2011

1
これに依存する前に、使用しているプラ​​ットフォームでテストすることをお勧めします。ヌルバイト書き込む必要がある場合でも、そうでない実装に遭遇したことはわかっています(古いMSランタイムを使用していたMinGWで発生した可能性があります)。
dmitri

10

C標準によれば、しない限り、バッファサイズが0であり、vsnprintf()そしてsnprintf()nullは、その出力を終了します。

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

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

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

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

TR 24731安全機能を使用していますか?への回答にも注意してください。(MicrosoftバージョンのMSDNを参照vsprintf_s())および安全でないC標準ライブラリ関数の安全な代替手段についてはMacソリューション?


ああ、邪悪な、それについて考えたことはありません。一方... :)
ファルケン教授

ああ、私は)(MSのvsprintfのだと思い、私をビット、と私はそれを拾った- 1つの習慣
教授ファルケン

4

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


XPが10年以上前にリリースされたことに気づきました。:-)
ファルケン教授2011年

そして今年は廃止されました。:)
ファルケン教授2014年

4

あいまいさはC標準自体から始まります。C99とC11はどちらもsnprintf機能の説明が同じです。これがC99からの説明です:

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

一方では、文

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


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

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

一方
、最後の文

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

あいまいさを与えます(または私の英語は十分ではありません)。私は、少なくとも2つの方法でこの文を解釈することができます:
1.出力されている、NULLで終了する場合は、返された値が非負とされている場合にのみ未満n返された値がされた場合にどの手段(ない未満n、すなわち出力(含みますnull文字を終了する)が配列に収まらない場合、出力はnullで終了しません)。
2.戻り値が負でなく、未満の場合にのみ、出力は完了します(文字は破棄されていません)。n


上記の解釈1は答えと矛盾し、誤解と長い議論を引き起こすと私は信じています。そのため、snprintf関数を説明する最後の文は、あいまいさを取り除くために変更が必要です(これにより、C言語標準への提案を作成する根拠が得られます)。リンクの@ "Martin Ba"のおかげで、
あいまいでない表現の例はhttp://en.cppreference.com/w/c/io/fprintf(を参照4))から取得できます。

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


4
あなたの解釈1は、私にはまったくもっともらしいとは思えません。私はその文を「出力(ちなみに、nullで終了する)は完全に書かれています...」と解析します。これは#2としてしか理解できません。
zwol 2018年

1
「nullで終了する出力が完全に書き込まれました」という文の否定は、「nullで終了する出力が完全に書き込まれていません」です。これ以上何もない。否定された文自体は、何かが書かれたことを意味するものではありません(これには、不完全なnullで終了する出力、不完全なnullで終了しない出力、または無色の緑色のアイデアが含まれます)。標準の他の場所は、出力が不完全な場合に正確に何が書き込まれるかを示しその場所は、出力が空でない限り(n == 0)、出力がnullで終了することを示します。
N。「代名詞」m。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.