20

snprintf を使用して、文字列を char 配列に連結しています。

char buf[20] = "";
snprintf(buf, sizeof buf, "%s%s", buf, "foo");
printf("%s\n", buf);
snprintf(buf, sizeof buf, "%s%s", buf, " bar");
printf("%s\n", buf);

buf問題は、を追加する代わりに"bar"への 2 番目の連結を"foo"行うことです。出力は次のようになります。

foo
bar

最初はそこに保持する%s必要がありますbuf(この場合は保持されます"foo")。そして2番目はそれに%s添付"bar"する必要があります。右?

私は何を間違っていますか?

4

4 に答える 4

32

restrictの契約に違反しています。snprintfこれは、他の引数がバッファをオーバーラップできないことを示しています。

とにかく、入力をそれ自体にコピーするのは労力の無駄です。 snprintfフォーマットに必要な文字数を返すので、これを利用して追加します。

char buf[20] = "";
char *cur = buf, * const end = buf + sizeof buf;
cur += snprintf(cur, end-cur, "%s", "foo");
printf("%s\n", buf);
if (cur < end) {
    cur += snprintf(cur, end-cur, "%s", " bar");
}
printf("%s\n", buf);
于 2012-08-22T01:42:54.160 に答える
3

これを試して:

char buf[20];
snprintf(buf, sizeof buf, "%s", "foo");
printf("%s\n", buf);
int len = strlen(buf);
snprintf(buf+len, (sizeof buf) - len, "%s", " bar");
printf("%s\n", buf);

出力は「foo bar」です。char へのポインターである snprintf の最初の引数は、文字の詰め込みを開始する場所です。すでにバッファにあるものには注意を払いません。ただし、関数 strlen には注意が必要です。snprintf がそこに置いた nul (0) の前の文字数をカウントします。したがって、buf を渡す代わりに、buf+strlen(buf) を渡します。strncat を使用することもできます。これは、わずかに効率的です。

あなたの質問の下に C++ というタグがあります。std::string を検索します。ずっと良い。

于 2012-08-22T01:52:56.563 に答える
3

受け入れられた答えは大丈夫ですが、(私の意見では)より良い答えは、文字列の連結が間違っているということです。への1 回の呼び出しで出力全体を構築する必要がありますsnprintf。これが、フォーマットされた出力関数を使用することの要点であり、ポインター演算や複数の呼び出しを行うよりもはるかに効率的で安全です。例えば:

snprintf(buf, sizeof buf, "%s%s%s", str_a, str_b, str_c);
于 2012-08-22T02:28:00.447 に答える
2

なぜ使用しないのstrncat()ですか?まさにこれを行うように設計されました:

char buf[20] = "";
strncat(buf, "foo", sizeof buf);
printf("%s\n", buf);
strncat(buf, " bar", sizeof buf - strlen(buf));
printf("%s\n", buf);

システムでサポートされている場合は、オーバーフロー保護のレベルが追加され、出力バッファーに残っているバイト数を計算する必要がなくなるため、strncat_s()代わりに使用できます。strncat

を使用する必要がある場合snprintfは、文字列の末尾を追跡するために別のポインターを作成する必要があります。このポインターは、に渡す最初の引数になりますsnprintf。現在のコードは常に を使用しbufています。つまり、常にその配列の先頭に出力されます。strlenを使用して、各呼び出しの後に文字列の末尾を見つけるかsnprintf、戻り値を使用しsnprintfてポインターをインクリメントすることができます。

于 2012-08-22T01:54:00.193 に答える