programing

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

javaba 2022. 7. 10. 11:29
반응형

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 경우는, 「GNU」asprintf():

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

Runtime을 수 없는 경우 MS C Runtime을 해야 합니다._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

여기 광기가 있어요. 제가 가서 재봤어요.젠장, 상상해봐뭔가 의미 있는 결과가 나온 것 같아요.

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

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

그리고 malloc과 루프 내부를 자유롭게 추가했습니다.그 결과 방법 1이 4.4배 느려졌다.방법 2는 1.1배 느려졌다.

따라서 malloc + strlen + free는 sprintf를 피할 만큼 프로파일을 지배하지 않습니다.

다음은 제가 사용한 코드입니다(루프는 !=가 아닌 <로 구현되었지만 이 게시물의 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;
}

두 방법 모두 문자열 길이를 계산하거나 매번 검색해야 하므로 매우 효율적이지 않습니다.대신 각 문자열의 strlen()을 계산하기 때문에 변수 내에 넣은 후 strncpy()를 두 번 입력합니다.

효율성에 대해 걱정할 필요가 없습니다.코드를 읽기 쉽고 유지 보수할 수 있도록 합니다.이 방법들의 차이가 당신의 프로그램에서 문제가 될지는 의문입니다.

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 format-string을 해석하는 오버헤드가 있습니다(인정할 수 있듯이 작음).그리고 스트캣은 "인라인 불가능"할 가능성이 높습니다.

sprintf()는 문자열뿐만 아니라 strcat()도 전문적으로 처리하도록 설계되어 있습니다.하지만 나는 네가 사소한 일에 땀을 흘리고 있다고 의심한다.C 문자열은 기본적으로 비효율적이며, 이러한 두 가지 제안된 방법 간의 차이를 중요하지 않게 만듭니다.유혈사태에 대한 자세한 내용은 조엘 스폴스키의 "Back to Basics"를 읽어보십시오.

이것은 일반적으로 C++가 C보다 더 나은 성능을 발휘하는 인스턴스입니다.무거운 무게의 스트링을 취급할 경우 std::string을 사용하는 것이 더 효율적이고 안전할 수 있습니다.

[편집]

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

std::string이 느리다는 Andrew Bainbridge의 코멘트에 놀랐습니다만, 이 테스트 케이스의 완전한 코드를 게재하지 않았습니다.나는 그의 (타이밍 자동화)를 수정하고 std:: 문자열 테스트를 추가했습니다.테스트는 VC++ 2008(네이티브 코드), 디폴트의 「Release」옵션(최적화), Athlon 듀얼 코어, 2.6GHz로 실시했습니다.결과:

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

따라서 여기서 strcat()은 C 문자열 규칙의 본질적인 비효율성에도 불구하고 훨씬 더 빠릅니다(사용자의 밀레이지는 컴파일러와 옵션에 따라 다를 수 있습니다).또한 sprintf()는 이 목적에 필요하지 않은 많은 짐을 운반한다는 저의 원래 제안을 뒷받침합니다.단, 가장 읽기 쉽고 안전성이 낮기 때문에 퍼포먼스가 중요하지 않은 경우에는 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;
}

두 번째 경우에 진짜 연결이 되는 건지는 모르겠지만인쇄는 연결을 구성하지 않습니다.

어느 쪽이 더 빠를지 알려주세요.

1) a) 문자열 A를 새로운 버퍼에 복사 b) 문자열 B를 버퍼에 복사 c) 출력 버퍼에 복사

또는

1) 문자열 A를 출력 버퍼에 복사 b) 문자열 b를 출력 버퍼에 복사

  • strcpy 및 strcat은 sprintf에 비해 훨씬 단순한 연산입니다.이 경우 형식 문자열을 해석해야 합니다.
  • strcpy 및 strcat은 작기 때문에 일반적으로 컴파일러에 의해 인라인화되므로 추가 함수 호출 오버헤드가 1개라도 절감됩니다.예를 들어, llvm strcat은 strlen을 사용하여 복사 시작 위치를 찾은 후 간단한 저장 명령이 이어집니다.

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

반응형