snprintf の返値について
#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;
}
上記のプログラムを実行した場合、
- 指定されたサイズ-1以降は破棄され書き込まれない
- いずれにせよヌル文字が書き込まれる。
- 実際に書き込まれた文字数(ヌル文字は除く)が返される
という認識でしたが、
実際にGCC(gcc-4.6.3)とclang(3.6.2)で試してみたところ
gcc の場合
abcd
7
gcc 5.1 in ideone の結果 C99 strictも同じ
clang 3.7 の場合も同じ
clang(3.6.2) の場合
abcde
-1
となりました。
clang(3.6.2)の結果は私の認識的にはおかしいですが、
このような場合に-1になるというのはそれはそれで正しいような気がします。
C11ドラフトの返値の部分によると
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.
となっていて、
自分なりに訳すと、
「指定サイズが十分に大きい場合ヌル終端を除いた文字数を返し、
エンコードエラー(具体的には何でしょう?)が起こった場合は-1をかえす。
それで、返値が負数でなくてかつ指定サイズより小さい場合のみヌル終端が完全に書き込まれます。」(解釈が間違っていたら教えて下さい)
つまり指定サイズが0でない場合でも、
指定したサイズよりも大きい値が返ることが許されていて(というか実際に書き込んだ文字数ではなく書き込みに必要なサイズが返されることが要求されていて、このような場合に実際に書き込んだ文字数(4)が返ることはない?)、
それはヌル終端していない可能性があり、使用に供さないということですか?
動作説明の部分によると
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.
となっていて、
「指定サイズ-1を超える出力文字は配列に書き込まれるのではなく破棄され、最後にヌル文字が書き込まれる。」
ということで最後にヌル文字が書き込まれることが保証されているように思えます。
規格で言う「完全に書き込まれた」は、指定した引数が指定した書式で書き込まれたと言うだけの意味(逆に言えばサイズが足りない場合は捨てるけど文字列は有効)ではなくて、返値が指定サイズ-1より大きい場合は無効な文字列(単に入らない部分が捨てられただけでなくCの文字列として使用できない、例えばヌル終端してない)を意味しますか?
私的には後者の場合かなりイメージと異なります。
このような場合常に返値が指定サイズ未満であることをチェックする必要が生じると思います。
質問の要点は次のようになります。
- snprintfは、実際に書き込んだ文字数(NULを除いて4)ではなく書き込みに必要な文字数(NULを除いた7)を返す?
- 返値が指定サイズ-1を超える場合はその出力文字列は使用不可?
補助的な質問
- エンコードエラーとは具体的にどのようなもの?
- 逆に常に出力に必要なサイズが返されるとは限らないとしたら、(サイズに0を指定した時とか返値を調べて足りない分をreallocで拡張するなどに)その返値は必要文字サイズとして利用することは無効?