Проблема в strncpy с исходной строкой длиннее целевого массива - PullRequest
0 голосов
/ 16 апреля 2020

Я пытался придумать, как сделать функцию strncpy и столкнулся с этой проблемой.

    char src[] = "123456789A";
    char dest[10];
    int n = 10;
    printf("strncpy:%s\n", strncpy(dest, src, n));

Вывод

strncpy:123456789A123456789A

Что происходит?

Ответы [ 3 ]

1 голос
/ 16 апреля 2020

Быстрый ответ: strncpy - не ваш друг!

strncpy - не безопаснее версия strcpy, он скопирует до n символов из src и, если src короче, заполнит место назначения нулевыми байтами в общей сложности n символов.

Если исходная строка имеет n или более символов, массив назначения не будет нулевым завершением , а передача на printf("%s", будет иметь неопределенное поведение : printf будет продолжать чтение и печать байтов из памяти после конец dest, пока он не найдет нулевой байт или пока это неопределенное поведение не вызовет другие непредсказуемые побочные эффекты ...

Семантика strncpy нелогична и подвержена ошибкам, избегайте этого функция. См. Эту статью для длинного ответа: https://randomascii.wordpress.com/2013/04/03/stop-using-strncpy-already/

1 голос
/ 16 апреля 2020

Как уже говорили другие, strncpy не будет содержать завершающий ноль, если размер назначения равен длине строки. Чтобы дать вам практический ответ, я обычно просто вычитаю один из размера пункта назначения, используя sizeof, чтобы получить размер пункта назначения, включая пространство для терминатора:

char src[] = "123456789A";
char dest[10];
printf("strncpy:%s\n", strncpy(dest, src, sizeof(dest) - 1));

, который дает вывод "strncpy: 123456789 msgstr "который является символом, кратким тому, что вы хотите, но, по крайней мере, является определенным поведением и позволяет вам знать, что целевой буфер недостаточно велик, чтобы содержать нулевой терминатор. Таким образом, окончательный код, который даст вам результат, который вы получите, будет:

char src[] = "123456789A";
char dest[11];
printf("strncpy:%s\n", strncpy(dest, src, sizeof(dest) - 1));
1 голос
/ 16 апреля 2020

Массив dest не содержит строку, так как недостаточно места для размещения завершающего нуля '\0' скопированной исходной строки,

Поэтому для вывода массива используйте следующую инструкцию

printf("strncpy: %*.*s\n", n, n, strncpy(dest, src, n));

В противном случае вы должны написать что-то вроде следующего

strncpy( dest, src, n )[sizeof( dest ) - 1] = '\0';

printf("strncpy: %s\n", dest );

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

Если вы хотите скопировать меньше символов, чем размер целевого массива, то что делать после копирования, зависит от намерения. Если вы хотите просто перезаписать часть строки, которая уже сохранена в целевом массиве, вам больше ничего не нужно делать. В противном случае установите символ в позиции n равным нулю.

Вот демонстрационная программа.

#include <stdio.h>
#include <string.h>

int main(void) 
{
    char src[] = "123456789A";
    char dest[10] = "543216789";

    size_t n = 5;

    strncpy( dest, src, n );

    printf("strncpy: %s\n", dest );

    strncpy( dest, "Hello", n )[n] = '\0';

    printf("strncpy: %s\n", dest );

    return 0;
}

Его вывод

strncpy: 123456789
strncpy: Hello
...