Если вы передаете destination
, ранее объявленный с автоматическим типом хранения или как указатель, ранее выделенный с malloc
, calloc
или realloc
с выделенным типом хранения, и в любом случае сnchars
свободного места, вы можете реализовать довольно надежную функцию copy
, просто используя snprintf
.
Поскольку вы передаете максимальное количество символов, включая nul-terminating символ в качестве параметра вашей copy
функции, которая прекрасно согласуется с параметром size
для snprintf
.Кроме того, snprintf
обеспечит строку с нулевым символом в конце в месте назначения - даже если source
слишком длинный, чтобы поместиться в destination
.В качестве преимущества, snprintf
возвращает количество скопированных символов, если в destination
достаточно места для source
, в противном случае возвращается количество символов , которое было бы скопировано было бы destination
достаточно места - позволяет вам определить количество усеченных символов, если destination
недостаточно для удержания вашей строки source
.
Перед рассмотрением реализации copy
давайте рассмотрим прототип и поговорим ообъявляя функции тем, что они обеспечивают значимый возврат, и пока вы сами, давайте также рассмотрим порядок параметров и спецификатора типа для source
, например,
/* simple strcpy src to dest, returns dest on success and number of chars
* (including nul-termining char) in nchar, returns NULL otherwise.
*/
char *copy (char *dest, const char *src, size_t *nchar);
Если вы заметили, большинство строковых функций возвращаютуказатель на строку назначения в случае успеха (или NULL
в противном случае), который позволяет немедленно использовать возврат.Далее, хотя это и не ограничитель показа, большинство строковых (или вообще памяти) функций копирования помещают destination
в качестве первого параметра, за которым следует source
.Я уверен, что либо Брайан Керриган, либо Деннис Ритчи могли бы объяснить почему, но достаточно сказать, что большинство параметров функции копирования упорядочены таким образом.
Обратите внимание, что, поскольку вы не изменяете строку source
в copy
, лучше всего квалифицировать параметр как const
.Квалификатор const
- это более или менее обещание, которое вы даете компилятору, что source
не будет изменено в copy
, что позволяет компилятору предупреждать, если он видит, что вы нарушаете это обещание, а также позволяет компилятору дополнительно оптимизироватьфункция, знающая, что source
не изменится.
Наконец, обратите внимание, что ваш size
или мой nchar
передается как указатель выше вместо непосредственного значения.Поскольку функция в C может возвращать только одно значение, если вам нужен способ вернуть второй фрагмент информации вызывающей стороне, передайте указатель в качестве параметра, чтобы значение по этому адресу можно было обновить в функции, создавая новыйзначение, доступное для вызывающей функции.Здесь вы возвращаете указатель на dest
(или NULL
), указывающий на успех / неудачу, а также обновляет nchar
, чтобы он содержал количество символов в dest
(включая символ с нулевым окончанием какВы указали размер , а не длина ).
Определение copy
довольно короткое и упрощенное.Единственное требование - строки source
и destination
не должны перекрываться.(в этом случае не определены ни strcpy
, ни snprintf
).Основной поток состоит в том, чтобы проверить , оба src
и dest
не NULL
, а затем обработать случай, когда src
- это "empty-string"
(например, 1-й символ - nul-), а затем скопировать src
в dest
, используя snprintf
, сохранив возврат в written
, а затем используя простое условное выражение, чтобы определить, произошло ли усечение (и предупреждение в этом случае), и в заключение обновивзначение, на которое указывает nchar
и возвращающее dest
, например,
/* simple strcpy src to dest, returns dest on success and number of chars
* (including nul-termining char) in nchar, returns NULL otherwise.
*/
char *copy (char *dest, const char *src, size_t *nchar)
{
if (!src || !dest) { /* validate src & dest not NULL */
fputs ("error: src or dest NULL\n", stderr);
return NULL; /* return NULL on error */
}
if (!*src) /* handle src being an "empty-string" */
*dest = 0, *nchar = 0;
int written = snprintf (dest, *nchar, "%s", src); /* call snprintf */
if ((size_t)written + 1 > *nchar) { /* handle truncated case */
fprintf (stderr, "warning: dest truncated by %zu chars.\n",
(size_t)(written + 1) - *nchar); /* warn with count */
}
else /* src fit in dest, set nchar to no. of chars in dest */
*nchar = (size_t)(written + 1); /* including nul-character */
return dest; /* return dest so available for immediate use */
}
В целом его можно привести в коротком примере, в котором для копирования в качестве первого аргумента программы используется строка (по умолчанию используется "source string"
)если аргумент не указан), вы можете сделать что-то вроде следующего:
#include <stdio.h>
#define MAXC 16 /* constant for destination length */
/* simple strcpy src to dest, returns dest on success and number of chars
* (including nul-termining char) in nchar, returns NULL otherwise.
*/
char *copy (char *dest, const char *src, size_t *nchar)
{
if (!src || !dest) { /* validate src & dest not NULL */
fputs ("error: src or dest NULL\n", stderr);
return NULL; /* return NULL on error */
}
if (!*src) /* handle src being an "empty-string" */
*dest = 0, *nchar = 0;
int written = snprintf (dest, *nchar, "%s", src); /* call snprintf */
if ((size_t)written + 1 > *nchar) { /* handle truncated case */
fprintf (stderr, "warning: dest truncated by %zu chars.\n",
(size_t)(written + 1) - *nchar); /* warn with count */
}
else /* src fit in dest, set nchar to no. of chars in dest */
*nchar = (size_t)(written + 1); /* including nul-character */
return dest; /* return dest so available for immediate use */
}
int main (int argc, char **argv) {
char *src = argc > 1 ? argv[1] : "source string",
dest[MAXC];
size_t n = MAXC;
if (copy (dest, src, &n))
printf ("dest: '%s' (%zu chars including nul-char)\n", dest, n);
}
( note: максимальное количество символов в dest
намеренно сокращено, чтобы легко показать, какобрабатывается усечение - размер соответствует вашим потребностям)
Пример использования / Вывод
$ ./bin/strcpy_snprintf
dest: 'source string' (14 chars including nul-char)
Отображение максимального количества символов, которое можно скопировать без предупреждения:
$ ./bin/strcpy_snprintf 123456789012345
dest: '123456789012345' (16 chars including nul-char)
Отображение источника обработки, слишком длинного для места назначения:
$ ./bin/strcpy_snprintf 1234567890123456
warning: dest truncated by 1 chars.
dest: '123456789012345' (16 chars including nul-char)
Просмотрите все и дайте мне знать, если выесть дополнительные вопросы.Существует как минимум дюжина различных способов приблизиться к копированию строки, но, учитывая, что вы передаете dest
со своим собственным хранилищем и передаете максимальное количество символов (включая nul-символ ) в качестве параметра,в этом случае трудно победить snprintf
.