59

文字列を連結する次の 2 つの方法に出会いました。

共通部分:

char* first= "First";
char* second = "Second";
char* both = malloc(strlen(first) + strlen(second) + 2);

方法 1:

strcpy(both, first);
strcat(both, " ");       // or space could have been part of one of the strings
strcat(both, second);

方法 2:

sprintf(both, "%s %s", first, second);

どちらの場合も、の内容はbothになります"First Second"

どちらがより効率的か (いくつかの連結操作を実行する必要があります)、またはそれを行うためのより良い方法を知っているかどうかを知りたいです。

4

10 に答える 10

73

読みやすくするために、私は

char * s = malloc(snprintf(NULL, 0, "%s %s", first, second) + 1);
sprintf(s, "%s %s", first, second);

プラットフォームが GNU 拡張機能をサポートしている場合は、以下も使用できますasprintf()

char * s = NULL;
asprintf(&s, "%s %s", first, second);

MS C ランタイムに行き詰まっている場合_scprintf()は、結果の文字列の長さを決定するために使用する必要があります。

char * s = malloc(_scprintf("%s %s", first, second) + 1);
sprintf(s, "%s %s", first, second);

次の方法がおそらく最速のソリューションです。

size_t len1 = strlen(first);
size_t len2 = strlen(second);

char * s = malloc(len1 + len2 + 2);
memcpy(s, first, len1);
s[len1] = ' ';
memcpy(s + len1 + 1, second, len2 + 1); // includes terminating null
于 2009-09-05T16:12:27.343 に答える
23

効率について心配する必要はありません。コードを読みやすく、保守しやすいものにしてください。これらの方法の違いがあなたのプログラムで重要になるとは思えません。

于 2009-09-05T15:56:26.440 に答える
6
size_t lf = strlen(first);
size_t ls = strlen(second);

char *both = (char*) malloc((lf + ls + 2) * sizeof(char));

strcpy(both, first);

both[lf] = ' ';
strcpy(&both[lf+1], second);
于 2009-09-05T16:14:46.667 に答える
2

違いが問題になる可能性は低いです。

  • 文字列が小さい場合、mallocは文字列の連結をかき消します。
  • 文字列が大きい場合、データのコピーに費やされる時間は、 strcat / sprintfの違いをかき消してしまいます。

他のポスターが述べているように、これは時期尚早の最適化です。アルゴリズムの設計に集中し、プロファイリングでパフォーマンスの問題であることが示された場合にのみ、これに戻ってください。

そうは言っても...方法1の方が速いと思いますsprintfformat -stringを解析するためのオーバーヘッドがいくつかあります---確かに小さい--- 。そして、strcatは「インライン化可能」である可能性が高くなります。

于 2009-09-05T16:31:55.567 に答える
2

それらはほとんど同じである必要があります。差はどうにもなりません。sprintf必要なコードが少ないので、私はそれを使います。

于 2009-09-05T15:58:38.923 に答える
1

sprintf() は単なる文字列以上のものを処理するように設計されており、strcat() はスペシャリストです。しかし、あなたは小さなことに汗を流しているのではないかと思います。C 文字列は、これら 2 つの提案された方法の違いが重要でないという意味で、根本的に非効率的です。詳細については、Joel Spolsky の「Back to Basics」を参照してください。

これは、一般的に C++ が C よりも優れたパフォーマンスを発揮する例です。重い文字列の処理では、std::string を使用した方が効率的で確実に安全です。

[編集]

[2 回目の編集] 修正されたコード (C 文字列の実装で反復が多すぎる)、タイミング、および結論がそれに応じて変更されます

std::string の方が遅いという Andrew Bainbridge のコメントには驚きましたが、彼はこのテスト ケースの完全なコードを投稿していませんでした。彼を修正し(タイミングを自動化)、std::string テストを追加しました。テストは、VC++ 2008 (ネイティブ コード) で、デフォルトの "Release" オプション (つまり、最適化)、Athlon デュアル コア、2.6GHz で行われました。結果:

C string handling = 0.023000 seconds
sprintf           = 0.313000 seconds
std::string       = 0.500000 seconds

したがって、ここで strcat() は、C 文字列規則の固有の非効率性にもかかわらず、はるかに高速です (マイレージはコンパイラとオプションによって異なる場合があります)。 . ただし、可読性と安全性がはるかに低いままであるため、パフォーマンスが重要でない場合、IMO のメリットはほとんどありません。

また、std::stringstream の実装もテストしましたが、これもはるかに遅くなりましたが、複雑な文字列の書式設定にはまだメリットがあります。

修正されたコードは次のとおりです。

#include <ctime>
#include <cstdio>
#include <cstring>
#include <string>

void a(char *first, char *second, char *both)
{
    for (int i = 0; i != 1000000; i++)
    {
        strcpy(both, first);
        strcat(both, " ");
        strcat(both, second);
    }
}

void b(char *first, char *second, char *both)
{
    for (int i = 0; i != 1000000; i++)
        sprintf(both, "%s %s", first, second);
}

void c(char *first, char *second, char *both)
{
    std::string first_s(first) ;
    std::string second_s(second) ;
    std::string both_s(second) ;

    for (int i = 0; i != 1000000; i++)
        both_s = first_s + " " + second_s ;
}

int main(void)
{
    char* first= "First";
    char* second = "Second";
    char* both = (char*) malloc((strlen(first) + strlen(second) + 2) * sizeof(char));
    clock_t start ;

    start = clock() ;
    a(first, second, both);
    printf( "C string handling = %f seconds\n", (float)(clock() - start)/CLOCKS_PER_SEC) ;

    start = clock() ;
    b(first, second, both);
    printf( "sprintf           = %f seconds\n", (float)(clock() - start)/CLOCKS_PER_SEC) ;

    start = clock() ;
    c(first, second, both);
    printf( "std::string       = %f seconds\n", (float)(clock() - start)/CLOCKS_PER_SEC) ;

    return 0;
}
于 2009-09-05T17:28:28.593 に答える
0

ケース2の場合、実際の連結が行われていることを私は知りません。それらを連続して印刷することは、連結を構成しません。

教えてください、どちらが速いでしょう:

1)a)文字列Aを新しいバッファにコピーしますb)文字列Bをバッファにコピーしますc)バッファを出力バッファにコピーします

また

1)文字列Aを出力バッファにコピーしますb)文字列bを出力バッファにコピーします

于 2009-09-05T16:23:12.633 に答える
-1

どちらの方法も文字列の長さを計算するか、毎回スキャンする必要があるため、どちらも非常に効率的ではありません。代わりに、とにかく個々の文字列の strlen() を計算するので、それらを変数に入れてから、strncpy() を 2 回だけ入れます。

于 2009-09-05T15:56:00.217 に答える