31649

[code]

    #include <stdio.h>
    
    int main(void){
        char s[5];
        int n = snprintf(s, sizeof(s), "%s", "abcdefg");
        printf("%s\n", s);
        printf("%d\n", n);
        return 0;
    }
    
[/code]

上記のプログラムを実行した場合、

  * 指定されたサイズ－１以降は破棄され書き込まれない
  * いずれにせよヌル文字が書き込まれる。
  * 実際に書き込まれた文字数(ヌル文字は除く)が返される

という認識でしたが、  
実際にGCC(gcc-4.6.3)とclang(3.6.2)で試してみたところ  
gcc の場合

[code]

    abcd
    7
    
[/code]

gcc 5.1 in ideone の[結果](http://ideone.com/3NHvy0) C99
strictも[同じ](http://ideone.com/D28mZB)  
clang 3.7 の場合も[同じ](http://ideone.com/x34p5B)

clang(3.6.2) の場合

[code]

    abcde
    -1
    
[/code]

となりました。  
clang(3.6.2)の結果は私の認識的にはおかしいですが、  
このような場合に－１になるというのはそれはそれで正しいような気がします。

Ｃ１１ドラフトの返値の部分によると

> The snprintf function returns the number of characters that would have  
>  been written had n been sufficiently large, not counting the  
>  terminating null character, or a negative value if an encoding error  
>  occurred. Thus, the null-terminated output has been completely written  
>  if and only if the returned value is nonnegative and less than n.

となっていて、

自分なりに訳すと、  
「指定サイズが十分に大きい場合ヌル終端を除いた文字数を返し、  
エンコードエラー（具体的には何でしょう？）が起こった場合は－１をかえす。  
それで、返値が負数でなくてかつ **指定サイズより小さい場合のみヌル終端が完全に書き込まれます** 。」（解釈が間違っていたら教えて下さい）

つまり指定サイズが０でない場合でも、  
指定したサイズよりも大きい値が返ることが許されていて（というか実際に書き込んだ文字数ではなく書き込みに必要なサイズが返されることが要求されていて、このような場合に実際に書き込んだ文字数（４）が返ることはない？）、  
それは **ヌル終端していない可能性があり、使用に供さない** ということですか？

動作説明の部分によると

> The snprintf function is equivalent to fprintf, except that the output  
>  is written into an array (specified by argument s) rather than to a  
>  stream. If n is zero, nothing is written, and s may be a null pointer.  
>  Otherwise, output characters beyond the n-1st are discarded rather  
>  than being written to the array, and a null character is written at  
>  the end of the characters actually written into the array. If copying  
>  takes place between objects that overlap, the behavior is undefined.

となっていて、  
「指定サイズ－１を超える出力文字は配列に書き込まれるのではなく破棄され、最後にヌル文字が書き込まれる。」  
ということで最後にヌル文字が書き込まれることが保証されているように思えます。

規格で言う「完全に書き込まれた」は、指定した引数が指定した書式で書き込まれたと言うだけの意味（逆に言えばサイズが足りない場合は捨てるけど文字列は有効）ではなくて、返値が指定サイズ－１より大きい場合は無効な文字列（単に入らない部分が捨てられただけでなくＣの文字列として使用できない、例えばヌル終端してない）を意味しますか？

私的には後者の場合かなりイメージと異なります。  
このような場合常に返値が指定サイズ未満であることをチェックする必要が生じると思います。

質問の要点は次のようになります。

  * snprintfは、実際に書き込んだ文字数(NULを除いて4)ではなく書き込みに必要な文字数(NULを除いた7)を返す？
  * 返値が指定サイズ－１を超える場合はその出力文字列は使用不可？

補助的な質問

  * エンコードエラーとは具体的にどのようなもの？
  * 逆に常に出力に必要なサイズが返されるとは限らないとしたら、(サイズに０を指定した時とか返値を調べて足りない分をreallocで拡張するなどに）その返値は必要文字サイズとして利用することは無効？


CC BY-SA 3.0
2017-01-05T21:05:21.987
31646
2017-01-11T07:59:32.377
2017-01-05T21:11:17.917
5044
5044
question
c c99
snprintf の返値について


言語仕様と１実装の示した挙動を混同しないでください。

>   * snprintfは、実際に書き込んだ文字数(NULを除いて4)ではなく書き込みに必要な文字数(NULを除いた7)を返す？
>

仕様に関する質問であれば仕様に記載されているとおり文字数を返すでしょう。具体的実装に関する質問であれば、それぞれの実装を確認してください。

>   * 返値が指定サイズ－１を超える場合はその出力文字列は使用不可？
>   * エンコードエラーとは具体的にどのようなもの？
>

手元の実装では`%999999999999999999999d`とした場合にフォーマットの解析中にオーバーフローして`-1`が返されていました。フォーマットが解析できていないので、結果文字列が作成できないということでしょう。  
このような場合があり得るため、`-1`が返された場合は出力文字列については期待するべきではないでしょう。

* * *

> 指定サイズ未満の場合についてしか書かれていないように思えます。

については

> The snprintf function returns the number of characters that would have been
> written had n been sufficiently large, not counting the terminating null
> character, or a negative value if an encoding error occurred.

を誤読しています。 「would have been written」なので **仮に`n`が十分に大きかった場合に書かれていたであろう文字数**
を返すことになっています。

また仕様にはカタカナの「エンコードエラー」ではなく「an encoding error
occurred」と書かれているわけですから、「encoding」「encode」について言及されている個所を確認されてみてはどうでしょうか？

* * *

質問からは脱線しますが重要なことなので

> 「clangコンパイラは準拠しているがその使用しているライブ‌​ラリは準拠していない」と言うのは（それが‌​正確なんでしょうけど）奇異に感じます。

C言語登場当時はOSがCコンパイラーおよびlibcを提供していました。その後、GCCやCLANGのようなクロスプラットフォームコンパイラーが登場したわけですが、これらはコンパイラーだけの存在であり、libcに関しては引き続きOSが提供しているものを使用しています。なぜならlibcは実装としてシステムコールを行う必要があるなどOSと密接に関わるからです。  
（その割にコンパイラーがlibcを呼び出さずにインライン展開してしまったり（例えば`memcpy`とか）と悩ましい場合もあります。）

ですので、コンパイラーがどのlibcを使用しているかについては常に把握する必要があります。  
例えばWindowsプラットフォーム向けGCCとしてcygwinとMinGWが有名ですが、[cygwinはPOSIX互換のlibcを独自に用意しているのに対しMinGWはMicrosoftが提供するVisual
C++向けlibcを使用している](https://ja.wikipedia.org/wiki/MinGW#Cygwin.E3.81.A8.E3.81.AE.E6.AF.94.E8.BC.83)という違いがあります。


CC BY-SA 3.0
2017-01-05T23:15:43.840
31647
2017-01-11T07:59:32.377
2017-01-11T07:59:32.377
4236
4236
31646
answer


> snprintfは、実際に書き込んだ文字数(NULを除いて4)ではなく書き込みに必要な文字数(NULを除いた7)を返す？

私の解釈では「YES」です。

C11 7.21.6.5, paragraph 3より引用：

> **Returns**  
>  The `snprintf` function returns the number of characters that would have
> been written had `n` been sufficiently large, not counting the terminating
> null character, [...] Thus, the null-terminated output has been completely
> written if and only if the returned value is nonnegative and less than `n`.

* * *

> 返値が指定サイズ－１を超える場合はその出力文字列は使用不可？

関数仕様上はNUL終端文字列を返すと保証するため、（狭義には）正しいC文字列として使用可能といえます。

C11 7.21.6.5, paragraph 2より引用：

> **Description**  
>  [...] Otherwise, output characters beyond the `n-1`st are discarded rather
> than being written to the array, and a null character is written at the end
> of the characters actually written into the array. [...]

広義の解釈としては、出力文字列の用途に依存します。例えば、参考情報の提示用途であれば末尾が欠落しても大した問題ではないでしょう。一方で、別プログラムが出力文字列を読み取るケースや、システム的に重要な情報提示目的であれば、欠落した出力文字列では使い物になりません。

* * *

> エンコードエラーとは具体的にどのようなもの？

文字列引数において「マルチバイト文字列として不正なエンコーディングが行われたとき」と考えられます。

C11 7.21.3, paragraph 14より引用：

> An _encoding error_ occurs if the character sequence presented to the
> underlying `mbrtowc` function does not form a valid (generalized) multibyte
> character, or if the code value passed to the underlying `wcrtomb` does not
> correspond to a valid (generalized) multibyte character. The wide character
> input/output functions and the byte input/output functions store the value
> of the macro `EILSEQ` in `errno` if and only if an encoding error occurs.

* * *

>
> 逆に常に出力に必要なサイズが返されるとは限らないとしたら、(サイズに０を指定した時とか返値を調べて足りない分をreallocで拡張するなどに）その返値は必要文字サイズとして利用することは無効？

関数仕様として成功時は「常に出力に必要なサイズが返され」ます。よって、戻り値が非負値ならば必要文字サイズとして利用できます。


CC BY-SA 3.0
2017-01-06T00:47:20.703
31649
2017-01-06T01:04:19.113
2017-01-06T01:04:19.113
49
49
31646
answer