Скажем, мы хотим объединить const char * s [0], s [1], ... s [n-1] в один длинный char out [] в C.
Формально (без учета переполнения буфера для простоты):
void concatManyStrings(char out[], const char *s[], size_t n);
Конечно, это тривиальная задача: начать с указателя на выход и продвигать его для каждого символа,
при просмотре входных строк.
Другой подход (который по-прежнему является линейным по времени) - сохранить указатель до конца,
и с каждым s [i] выполните:
{ strcpy(endp, s[i]); endp += strlen(s[i]); }
Но код был бы чище, если бы существовала стандартная функция CRT, которая знает, как strcpy()
,
и вернуть количество скопированных символов (или, что эквивалентно, указатель на следующий символ после скопированного).
Единственная функция CRT, которую я могу придумать, это sprintf()
, но это явно не
столь же эффективен, как простой strcpy()
, который возвращает счетчик.
Есть такая функция, которую мне не хватает?
3 ответа
strlcpy()
и strlcat()
, к сожалению, нестандартные, но если они у вас есть, вы можете использовать их для этого. Оба они возвращают результаты, позволяющие определить конец скопированной строки, в отличие от strcpy()
и strcat()
, которые (несколько бесполезно) возвращают указатель на начало места назначения.
Вы не можете позволить себе игнорировать переполнение буфера; это один из основных способов сбоя веб-мира.
Учитывая показанную структуру данных, есть предел тому, что вы можете делать. Если бы структура данных включала длины каждой из строк в данных, переданных в функцию, вы могли бы сделать больше. Однако до тех пор вы должны определить длину каждой строки (и указать длину выходного буфера), а затем организовать безопасное копирование строк. Поскольку к моменту копирования вы будете знать длину строки, вы можете использовать memmove()
или memcpy()
для перемещения данных, и вы знаете длину, чтобы можно было настроить указатель:
int concatManyStrings(char *buffer, size_t buflen, const char **data, size_t nitems)
{
assert(buflen > 0);
char *dst = buffer;
char *end = buffer + buflen - 1;
for (size_t i = 0; i < nitems; i++)
{
size_t len = strlen(data[i]);
if (dst + len >= end)
return -1;
memmove(dst, data[i], len);
dst += len;
}
*dst = '\0';
return 0;
}
Это сканирует каждую строку дважды - один раз для определения длины и один раз для копирования. Однако вы не можете позволить себе использовать strncpy()
из-за его поведения с заполнением нулями (дьявольским в данном контексте); тот факт, что это не гарантирует нулевое завершение, не будет проблемой. Вы не можете использовать strcpy()
, пока не узнаете, что длина безопасна, для чего требуется strlen()
. Если данные были не простым массивом указателей на строки, а массивом структуры, включающей длину строки, а также указатель, то strlen()
можно было бы избежать. С осторожностью можно использовать strcat()
или strncat()
; главное предостережение - избегать квадратичного поведения (алгоритм Шлемиля), которое может быть достигнуто, если вы определите конец каждой добавляемой строки. В случае strncat()
будьте очень осторожны с параметром размера; он отличается от размера strncpy()
. И вам все равно, вероятно, придется использовать strlen()
, поскольку стандартные функции не сообщают о конце строки, где они поместили последний символ - что было бы намного полезнее, чем возврат указателя на первый символ целевой строки. .
Я не знаю стандартной функции для этого.
Используйте snprintf
, который, по сути, всегда является правильным ответом на любой вопрос о сборке строк:
snprintf(buf, buflen, "%s%s%s", str1, str2, str3);
К сожалению, это не работает для "произвольного n
" в качестве счетчика входной строки; для этого просто напишите свой собственный цикл for ...
Похожие вопросы
Новые вопросы
c
C - это язык программирования общего назначения, используемый для системного программирования (ОС и встраиваемых), библиотек, игр и кроссплатформенности. Этот тег следует использовать с общими вопросами, касающимися языка C, как это определено в стандарте ISO 9899 (последняя версия 9899: 2018, если не указано иное, а также для запросов, специфичных для версии, с c89, c99, c11 и т. Д.). C отличается от C ++ и не должен сочетаться с тэгом C ++ без разумной причины.