Конкатенация строк в C, какой метод более эффективен?

Если Вы хотите IEnumerable<byte>, то всего

IEnumerable<byte> data = foo.Take(x);
56
задан Peter Cordes 5 December 2017 в 05:01
поделиться

9 ответов

Для удобства чтения я ' d пойти с

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
72
ответ дан 26 November 2019 в 17:14
поделиться

Не беспокойтесь об эффективности: сделайте ваш код читаемым и поддерживаемым. Я сомневаюсь, что разница между этими методами будет иметь значение в вашей программе.

24
ответ дан 26 November 2019 в 17:14
поделиться
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);
6
ответ дан 26 November 2019 в 17:14
поделиться

Они должны быть примерно такими же. Разница не имеет значения. Я бы выбрал sprintf , поскольку он требует меньше кода.

2
ответ дан 26 November 2019 в 17:14
поделиться

Вот вам какое-то безумие, я на самом деле пошел и измерил. Черт возьми, представь это. Думаю, я получил значимые результаты.

Я использовал двухъядерный процессор P4, работающий под управлением Windows, используя mingw gcc 4.4, построенный с помощью "gcc foo.c -o foo.exe -std = c99 -Wall -O2".

Я протестировал метод 1 и метод 2 из исходного сообщения. Изначально оставил malloc вне цикла тестирования. Метод 1 был в 48 раз быстрее, чем метод 2. Как ни странно, удаление -O2 из команды сборки сделало результирующий exe на 30% быстрее (еще не выяснил, почему).

Затем я добавил malloc и free внутри цикла. Это замедлило работу метода 1 в 4,4 раза. Метод 2 замедлился в 1,1 раза.

Итак, malloc + strlen + free НЕ ДОЛЖНЫ доминировать в профиле, чтобы избежать использования sprintf.

Здесь ' s код, который я использовал (кроме циклов, которые были реализованы с помощью <вместо! =, но это нарушило рендеринг 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;
}
18
ответ дан 26 November 2019 в 17:14
поделиться

Разница вряд ли будет иметь значение:

  • Если ваши строки маленькие, malloc заглушит конкатенации строк.
  • Если ваши строки большие, время, потраченное на копирование данных, заглушит различия между strcat / sprintf .

Как упоминалось в других плакатах, это преждевременная оптимизация. Сконцентрируйтесь на разработке алгоритма и возвращайтесь к нему только в том случае, если профилирование показывает, что это проблема производительности.

Тем не менее ... Я подозреваю, что метод 1 будет быстрее. Есть некоторые --- по общему признанию небольшие --- накладные расходы на синтаксический анализ строки формата sprintf . А strcat , скорее всего, "встроен в строку".

2
ответ дан 26 November 2019 в 17:14
поделиться

Я не знаю, что в случае двух будет какое-либо реальное объединение. Их последовательная печать не является объединением.

Скажите мне, что будет быстрее:

1) a) скопировать строку A в новый буфер б) скопировать строку B в буфер c) копировать буфер в выходной буфер

или

1) копировать строку A в выходной буфер б) скопировать строку b в выходной буфер

0
ответ дан 26 November 2019 в 17:14
поделиться

Ни один из них не очень эффективен, поскольку оба метода должны вычислять длину строки или сканировать ее каждый раз. Вместо этого, поскольку вы все равно вычисляете strlen () отдельных строк, поместите их в переменные, а затем дважды просто strncpy ().

-1
ответ дан 26 November 2019 в 17:14
поделиться

sprintf () предназначен для обработки гораздо большего, чем просто строки, strcat () является специалистом. Но я подозреваю, что вы потеете по мелочам. Строки C принципиально неэффективны в том смысле, что различия между этими двумя предложенными методами несущественны. Прочтите «Назад к основам» Джоэла Спольски, чтобы узнать о кровавых подробностях.

Это тот случай, когда C ++ обычно работает лучше, чем C. Для обработки тяжелой строки с использованием std :: string, вероятно, будет больше эффективнее и, безусловно, безопаснее.

[править]

[2-е правка] Исправленный код (слишком много итераций в реализации строки C), тайминги и выводы изменились соответственно

Я был удивлен комментарием Эндрю Бейнбриджа о том, что std :: string работает медленнее, но он не опубликовал полный код для этого тестового примера. Я изменил его (автоматизируя время) и добавил тест std :: string. Тест проводился на VC ++ 2008 (собственный код) с опциями «Release» по умолчанию (т.е. оптимизированными), двухъядерным процессором Athlon, 2,6 ГГц. Результаты:

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

Итак, здесь strcat () намного быстрее (ваш пробег может варьироваться в зависимости от компилятора и параметров), несмотря на присущую ему неэффективность соглашения о строках C, и поддерживает мое первоначальное предположение о том, что sprintf () несет большой багаж для этого не требуется. Однако он остается наименее читаемым и безопасным, поэтому, когда производительность не критична, ИМО имеет мало достоинств.

Я также тестировал реализацию 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
ответ дан 26 November 2019 в 17:14
поделиться
Другие вопросы по тегам:

Похожие вопросы: