Конкатенация строк в 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"
.
Я хотел бы знать, какой из них эффективнее (мне надо выполнить несколько операций конкатенации), или, если вы знаете лучший способ сделать это.
10 ответов:
для удобства чтения, я бы пошел с
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 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
Не беспокойтесь об эффективности: сделайте свой код читаемым и поддерживаемым. Я сомневаюсь, что разница между этими методами будет иметь в вашей программе.
вот вам какое-то безумие, я действительно пошел и измерил его. Черт возьми, представь себе это. Я думаю, что получил некоторые значимые результаты.
я использовал двухъядерный P4, работающий под управлением Windows, используя mingw gcc 4.4, строя с "gcc foo.к-о Фу.exe-std=C99-Wall-O2".
я протестировал метод 1 и Метод 2 из исходного сообщения. Первоначально держал malloc вне контрольного цикла. Метод 1 был в 48 раз быстрее, чем метод 2. Как ни странно, удаление -O2 из команды сборки сделало в результате exe на 30% быстрее (еще не исследовал, почему).
затем я добавил malloc и свободный внутри цикла. Это замедлило метод 1 в 4,4 раза. Метод 2 замедляется в 1,1 раза.
таким образом, malloc + strlen + free не доминируют в профиле достаточно, чтобы избежать sprintf стоит.
вот код, который я использовал (кроме циклов были реализованы с
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
Так как он требует меньше кода.
разница вряд ли имеет значение:
- Если ваши строки малы, то Танос заглушит конкатенации строк.
- Если ваши строки большие, время, потраченное на копирование данных, заглушит различия между strcat/ sprintf.
Как уже упоминалось в других плакатах, это преждевременная оптимизация. Сконцентрируйтесь на разработке алгоритма, и только вернуться к этому, если профилирование показывает, что это проблема производительности.
Что сказал... Я подозреваемый метод 1 будет быстрее. Есть некоторые - - - по общему признанию небольшие - - - накладные расходы для разбора sprintf. И strcat скорее всего, "встроенный".
sprintf() предназначен для обработки гораздо больше, чем просто строки, strcat () является специалистом. Но я подозреваю, что вы потеете по мелочам. Строки C принципиально неэффективны таким образом, что различия между этими двумя предлагаемыми методами незначительны. Читайте "назад к основам" Джоэл Спольски для кровавых деталей.
это экземпляр, где C++ обычно работает лучше, чем C. Для обработки строк с большим весом с помощью std:: string, вероятно, будет более эффективно и, конечно, безопаснее.
[edit]
[2nd edit]исправленный код (слишком много итераций в реализации строки C), тайминги и заключение соответственно меняются
Я был удивлен комментарием Эндрю Бейнбриджа о том, что std:: string был медленнее, но он не опубликовал полный код для этого тестового случая. Я изменил его (автоматизируя синхронизацию) и добавил тест std::string. Тест был на VC++ 2008 (собственный код) с по умолчанию опции "Release" (т. е. оптимизированы), Athlon dual core, 2.6 GHz. Результаты:
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) a) скопировать строку A в новый буфер b) скопировать строку B в буфер c) скопировать буфер в выходной буфер
или
1) скопируйте строку A в выходной буфер B) скопируйте строку b в выходной буфер
- strcpy и strcat-это гораздо более простые операции по сравнению с sprintf, который должен анализировать строку формата
- strcpy и strcat малы, поэтому они обычно будут встроены компиляторами, экономя еще один дополнительный вызов функции. Например, в llvm strcat будет встроен с помощью strlen, чтобы найти начальную позицию копирования, а затем простую инструкцию по хранению