3

私の答えの1つに対するコメントは、私を少し当惑させました。2 つの文字列を新しいメモリ ブロックに連結するために必要なメモリ量を計算しようとすると、以下に示すように、を使用するよりも を使用するsnprintf方が好まれると言われました。strlen

size_t length = snprintf(0, 0, "%s%s", str1, str2);
// preferred over:
size_t length = strlen(str1) + strlen(str2);

この背後にある理由を得ることができますか?あるとすれば、どのような利点がありますか? また、ある結果が他の結果と異なることはありますか?

4

7 に答える 7

5

発言したの+1は私で、早口でうっかり書いてしまったのでコメント欄に記載を省略させていただきましたので説明させてください。私の要点は、微妙に異なる可能性のある 2 つの異なる方法を使用するのではなく、同じ方法を使用して最終的に文字列を埋めるために使用される長さを計算するパターンを使用する必要があるということだけでした。

たとえば、2 つではなく 3 つの文字列があり、それらの 2 つ以上がオーバーラップしている場合、ゼロをstrlen(str1)+strlen(str2)+strlen(str3)+1超えてラップし、割り当て不足や出力の切り捨て (使用されている場合) または非常に危険なメモリが発生する可能性があります。破損 (とが使用されている場合)。SIZE_MAXsnprintfstrcpystrcat

snprintf結果の文字列が より長くなる-1とが返されるため、保護されます。ただし、使用する前に戻り値を確認し、null ターミネータ用に 1 つ追加する必要があります。errno=EOVERFLOWINT_MAX

于 2011-04-10T23:59:03.370 に答える
3

2 つの文字列を連結したときの大きさだけを判断する必要がある場合は、2 つの文字列snprintfの合計の長さを判断するための最小限の操作が 2 つのstrlen呼び出しで行われるため、 を優先する特別な理由はありません。snprintf文字をカウントする2つの文字列をたどるだけでなく、パラメーターをチェックしてフォーマット文字列を解析する必要があるため、ほぼ確実に遅くなります。

...しかし...snprintf 2 つの文字列を連結し、通常のケースを処理するには大きすぎない静的なバッファーを使用するシナリオで使用するのは賢明な方法かもしれ大きな文字列の場合に割り当てられたバッファ。例:

/* static buffer "big enough" for most cases */
char buffer[256];
/* pointer used in the part where work on the string is actually done */
char * outputStr=buffer;
/* try to concatenate, get the length of the resulting string */
int length = snprintf(buffer, sizeof(buffer), "%s%s", str1, str2);
if(length<0)
{
    /* error, panic and death */
}
else if(length>sizeof(buffer)-1)
{
    /* buffer wasn't enough, allocate dynamically */
    outputStr=malloc(length+1);
    if(outputStr==NULL)
    {
        /* allocation error, death and panic */
    }
    if(snprintf(outputStr, length, "%s%s", str1, str2)<0)
    {
        /* error, the world is doomed */
    }
}

/* here do whatever you want with outputStr */

if(outputStr!=buffer)
    free(outputStr);
于 2011-04-10T22:41:59.937 に答える
0

1 つの利点は、入力文字列が/ソリューションで 2 回ではなく、1 回(内で) スキャンされることです。snprintf()strlenstrcpy

実際、この質問と以前の回答に対するコメントを読み直すと、連結された文字列の長さを計算するsprintf()ためだけに使用する意味がわかりません。実際に連結を行っている場合は、上記の段落が適用されます。

于 2011-04-10T22:35:49.960 に答える
0

編集:ランダムで誤ったナンセンスが削除されました。私はそれ言いましたか?

編集:以下の彼のコメントのマッテオは絶対に正しく、私は絶対に間違っていました.

C99 から:

2 snprintf 関数は、出力がストリームではなく配列 (引数 s で指定) に書き込まれることを除いて、fprintf と同等です。n が 0 の場合、何も書き込まれず、s は null ポインターの可能性があります。それ以外の場合、n-1 番目を超える出力文字は配列に書き込まれるのではなく破棄され、実際に配列に書き込まれる文字の最後に null 文字が書き込まれます。重複するオブジェクト間でコピーが行われる場合、動作は未定義です。

戻り値 3 snprintf 関数は、n が十分に大きければ書き込まれたであろう文字数を返します。エンコーディング エラーが発生した場合は負の値を返します。したがって、戻り値が非負で n より小さい場合にのみ、ヌル終了出力が完全に書き込まれます。

ありがとう、マテオ、そして私はOPに謝罪します.

これは素晴らしいニュースです。わずか 3 週間前に私がここで尋ねた質問に肯定的な回答が得られたからです。すべての回答を読まなかった理由を説明することはできません。素晴らしい!

于 2011-04-10T22:38:19.697 に答える
0

strlen() の例に 1 を追加する必要があります。nul 終端バイトにスペースを割り当てる必要があることに注意してください。

于 2011-04-10T22:43:40.257 に答える
0

ここでわかる「利点」はstrlen(NULL)、セグメンテーション違反を引き起こす可能性がある一方で、(少なくとも glibc の)失敗することなくパラメーターをsnprintf()処理することです。NULL

したがって、glibcを使用すると、(少なくとも私のシステムでは)何も表示されずに "(null)" が出力されるためsnprintf()、文字列の 1 つが であるかどうかを確認する必要はありませんがNULL、必要以上に大きくなる可能性があります。lengthprintf("%s", NULL);


snprintf()代わりに 使用することはお勧めしませんstrlen()。それは明らかではありません。はるかに優れた解決策はstrlen()、引数が の場合に 0 を返すラッパーNULLです。

size_t my_strlen(const char *str)
{
    return str ? strlen(str) : 0;
}
于 2011-04-10T22:59:02.260 に答える
0

したがって、snprintf( ) は文字列のサイズを教えてくれます。つまり、その人のためにスペースを malloc( ) できるということです。非常に便利です。

後で出力するために大量の文字列をフォーマットするため、snprintf() のこの関数が必要でした (ただし、今まで見つかりませんでした)。しかし、出力の長さを予測するのは難しいため、出力に静的バッファを割り当てる必要はありませんでした。そのため、多くの 4096 の長さの char 配列になってしまいました :-(

しかし今では、(私にとって) 新たに発見されたこの snprintf( ) 文字カウント関数を使用して、malloc( ) 出力バッファ夜間スリープの両方を実行できます。

OPとMatteoに感謝し、謝罪します。

于 2011-04-10T23:07:34.600 に答える