UFO ET IT

C에서 문자열을 연결하면 어떤 방법이 더 효율적입니까?

ufoet 2020. 12. 29. 07:34
반응형

C에서 문자열을 연결하면 어떤 방법이 더 효율적입니까?


문자열을 연결하는 다음 두 가지 방법을 발견했습니다.

공통 부분 :

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".

어느 것이 더 효율적인지 (여러 연결 작업을 수행해야 함) 또는 더 나은 방법을 알고 싶습니다.


가독성을 위해 나는

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

효율성에 대해 걱정하지 마십시오. 코드를 읽기 쉽고 유지 관리 할 수 ​​있도록 만드십시오. 이 방법의 차이점이 프로그램에서 중요 할 것 같지 않습니다.


여기 당신을위한 약간의 광기가 있습니다. 저는 실제로 그것을 측정했습니다. 빌어 먹을 지옥, 상상 해봐. 의미있는 결과를 얻은 것 같아요.

나는 "gcc foo.c -o foo.exe -std = c99 -Wall -O2"로 빌드하고 mingw gcc 4.4를 사용하여 Windows를 실행하는 듀얼 코어 P4를 사용했습니다.

원래 게시물에서 방법 1과 방법 2를 테스트했습니다. 처음에는 malloc을 벤치 마크 루프 외부에 두었습니다. 방법 1은 방법 2보다 48 배 더 빠릅니다. 이상하게도 빌드 명령에서 -O2를 제거하면 결과 exe가 30 % 더 빨라졌습니다 (아직 이유를 조사하지 않았습니다).

그런 다음 malloc을 추가하고 루프 내부를 해제했습니다. 이로 인해 방법 1이 4.4 배 느려졌습니다. 방법 2는 1.1 배 느려졌습니다.

따라서 malloc + strlen + free는 스프린트를 피할만큼 충분히 프로필을 지배하지 마십시오.

내가 사용한 코드는 다음과 같습니다 (루프가! = 대신 <로 구현되었지만이 게시물의 HTML 렌더링이 깨졌습니다).

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

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

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

    // Takes 3.7 sec with optimisations, 2.7 sec WITHOUT optimisations!
    a(first, second, both);

    // Takes 3.7 sec with or without optimisations
    //b(first, second, both);

    return 0;
}

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);

거의 동일해야합니다. 차이는 중요하지 않습니다. 나는 함께 갈 것이다sprintf더 적은 코드가 필요하기 때문에 .


차이는 중요하지 않습니다.

  • 문자열이 작 으면 malloc 이 문자열 연결을 제거합니다.
  • 문자열이 크면 데이터를 복사하는 데 소요되는 시간이 strcat / sprintf 의 차이를 없애줍니다. .

다른 포스터에서 언급했듯이 이것은 조기 최적화입니다. 알고리즘 설계에 집중하고 프로파일 링에서 성능 문제로 확인되는 경우에만이 문제로 돌아 오십시오.

그게 내가 ... 말했다 의심 방법 1 빨라집니다. sprintf 형식 문자열 을 구문 분석하는 데 약간의 오버 헤드가 있습니다. 그리고 strcat 은 "인라인 가능"가능성이 더 높습니다.


sprintf ()는 단순한 문자열 이상을 처리하도록 설계되었으며 strcat ()은 전문가입니다. 그러나 나는 당신이 작은 것들에 땀을 흘리고 있다고 생각합니다. C 문자열은이 두 가지 제안 된 방법 간의 차이를 중요하지 않게 만드는 방식에서 근본적으로 비효율적입니다. 자세한 내용은 Joel Spolsky의 "Back to Basics"를 읽어보십시오 .

이것은 C ++가 일반적으로 C보다 더 나은 성능을 발휘하는 경우입니다. std :: string을 사용하는 무거운 문자열 처리의 경우 더 효율적이고 확실히 안전 할 수 있습니다.

[편집하다]

[2 차 편집] 수정 된 코드 (C 문자열 구현에서 너무 많은 반복), 타이밍 및 결론이 그에 따라 변경됨

I was surprised at Andrew Bainbridge's comment that std::string was slower, but he did not post complete code for this test case. I modified his (automating the timing) and added a std::string test. The test was on VC++ 2008 (native code) with default "Release" options (i.e. optimised), Athlon dual core, 2.6GHz. Results:

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

So here strcat() is faster by far (your milage may vary depending on compiler and options), despite the inherent inefficiency of the C string convention, and supports my original suggestion that sprintf() carries a lot of baggage not required for this purpose. It remains by far the least readable and safe however, so when performance is not critical, has little merit IMO.

I also tested a std::stringstream implementation, which was far slower again, but for complex string formatting still has merit.

Corrected code follows:

#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;
}

I don't know that in case two there's any real concatenation done. Printing them back to back doesn't constitute concatenation.

Tell me though, which would be faster:

1) a) copy string A to new buffer b) copy string B to buffer c) copy buffer to output buffer

or

1)copy string A to output buffer b) copy string b to output buffer


  • strcpy and strcat are much simpler oprations compared to sprintf, which needs to parse the format string
  • strcpy and strcat are small so they will generally be inlined by the compilers, saving even one more extra function call overhead. For example, in llvm strcat will be inlined using a strlen to find copy starting position, followed by a simple store instruction

Neither is terribly efficient since both methods have to calculate the string length or scan it each time. Instead, since you calculate the strlen()s of the individual strings anyway, put them in variables and then just strncpy() twice.

ReferenceURL : https://stackoverflow.com/questions/1383649/concatenating-strings-in-c-which-method-is-more-efficient

반응형