Почему strncpy () не соблюдает заданный size_t n, равный 10 в temp2?

Эта проблема поражает меня ... Может кто-нибудь, пожалуйста, разобраться с проблемой, потому что я уже потратил часы на это ..; (

#include <stdio.h>
#include <string.h>
int main(){

  char string[] = "Iam pretty much big string.";
  char temp1[50];
  char temp2[10];

  // strcpy() and strncpy()
   strcpy(temp1, string);
   printf("%s
", temp1);

  strncpy(temp2, temp1, 10);
  printf("%s
", temp2);
  return 0;
}

Результат

Iam pretty much big string.
Iam prettyIam pretty much big string.

Ожидаемый результат:

Iam pretty much big string.
Iam pretty

Всего 5 ответов


Функция strncpy соблюдает установленный вами предел в 10 байт.

Он копирует первые 10 байтов из string в temp2 . Ни один из этих 10 байтов не является нулевым байтом, а размер temp2 равен 10, поэтому в temp2 нет нулевых байтов. Когда вы затем передаете temp2 в printf , он читает после конца массива, вызывая неопределенное поведение .

Вам необходимо установить размер, заданный для strncpy размеру массива - 1, а затем вручную добавить нулевой байт в конец.

strncpy(temp2, temp1, sizeof(temp2)-1);
temp2[sizeof(temp2)-1] = 0;

Адрес temp2 находится непосредственно перед адресом temp1 и, поскольку вы не копируете последний 0 , printf продолжит печать после окончания temp2.

Как только вы не вставите 0 , результат printf будет неопределенным.


Вы вызываете неопределенное поведение, пытаясь напечатать temp2 как temp2 не завершается temp2 . От man strncpy :

« Предупреждение: если среди первых n байтов src нет нулевого байта, строка, помещенная в dest, не будет заканчиваться нулем.» (акцент в оригинале)

См. Также Стандарт C11 - 7.24.2.4 Функция strncpy (в частности, сноска: 308)

Так что temp2 не имеет temp2 завершения.


Из справочной страницы для strncpy() :

Предупреждение : если среди первых n байтов src нет нулевого байта, строка, помещенная в dest, не будет заканчиваться нулем.

Либо ваш ввод короче указанной длины, либо вы добавляете завершающий нулевой байт самостоятельно, либо его там не будет. printf() ожидает, что строка будет правильно завершена нулем, и, таким образом, заполняет ваш выделенный буфер.

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


Ссылка на соответствующий тег [strncpy] в переполнении стека https://stackoverflow.com/tags/strncpy/info , который может помочь вам понять, что именно происходит:


Эту функцию не рекомендуется использовать для каких-либо целей, ни в C, ни в C ++. Он никогда не задумывался как «безопасная версия strcpy », но часто используется для таких целей. На самом деле он считается гораздо более опасным, чем strcpy , так как нулевой механизм завершения strncpy не интуитивен и поэтому часто неправильно понимается. Это из-за следующего поведения, определенного ISO 9899: 2011 7.24.2.4:

char *strncpy(char * restrict s1, 
     const char * restrict s2, 
     size_t n);

/ - /

3 Если массив, на который указывает s2, является строкой, которая короче, чем n символов, нулевые символы добавляются к копии в массиве, на который указывает s1 , до тех пор, пока все n символов не будут записаны.

Очень распространенной ошибкой является передача s2 который является точно таким же количеством символов, что и параметр n , и в этом случае s1 не завершится нулем. То есть: strncpy(dst, src, strlen(src));

/* MCVE of incorrect use of strncpy */
#include <string.h>
#include <stdio.h>

int main (void)
{
  const char* STR = "hello";
  char buf[] = "halt and catch fire";
  strncpy(buf, STR, strlen(STR));
  puts(buf); // prints "helloand catch fire"
  return 0;
}

Рекомендуемая практика в C состоит в том, чтобы заранее проверить размер буфера и затем использовать strcpy() , или memcpy() . Рекомендуемая практика в C ++ - использовать вместо std::string .


Есть идеи?

10000