strncpy, приводящий к ошибке сегментации

Я просто возился с strncpy.

Моя программа выглядит так:

typedef struct
{
    char from_str[10];
}test;

main ()
{

    test     s1;
    memset(&s1,0,sizeof(test));
    char       src[10]="himansh";
    char       dest[10];

    memset(dest,0,10);
    src[3]='\0';

    printf("src is %s and strlen is %d \n",
            src,strlen(src));

    fflush(stdout);

    strncpy(s1.from_str,src,100);

    printf("s1.from_str is %s , src is %s \n",
            s1.from_str,src);
    return 1;

}

Здесь, прежде чем я сделаю strncpy, я добавил символ «\ 0» в строке «src», длина строки «src» становится 3, целевой массив имеет размер 10. Но в strncpy я поставил количество байтов, которые будут скопированы как 100 ,

Это означает, что моя исходная строка завершена NULL. Теперь strncpy, как и любая строковая функция, должен попытаться скопировать только 3 байта, даже если количество байтов, которые я предоставляю, больше 3 (в этом случае 100). Он делает это, но у меня также возникает ошибка сегментации.

Мой результат показан ниже

src is him and strlen is 3
s1.from_str is him , src is him
Segmentation fault (core dumped)

Почему эта ошибка сегментации происходит здесь.

Может кто-нибудь помочь мне здесь.

8
nl ja de
То, что вы думаете, что оно должно делать, и что это такое, - это две совершенно разные вещи.
добавлено автор Brian Roach, источник

4 ответы

Я мог бы указать вам на man-страницы, веб-сайты и т. Д., Но в конечном итоге важно, что сам C-стандарт. Как часть стандартной библиотеки времени выполнения, использование и поведение определены в C99-§7.23.2.4 следующим образом:

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

<�сильный> Описание   Функция strncpy копирует не более n символов (символы, которые следуют за нулевым символом, не копируются) из массива, на который указывает s2, на массив, на который указывает   s1. Если копирование происходит между перекрывающимися объектами, поведение не определено.   Если массив, на который указывает s2, является строкой, которая короче, чем n символов, нулевые символы добавляются к копии в массиве, на который указывает s1, до тех пор, пока не будут записаны все n символов.

     

Возвращает   Функция strncpy возвращает значение s1.

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

Более того, хотя это четко указано в стандарте (см. Выше), он продолжает смешивать меня, сколько инженеров НЕ знают, что strncpy() хвост заполняет буфер строки назначения с помощью до тех пор, пока указанная длина n не будет достигнута, если длина исходной строки меньше , чем размер целевого буфера. Это делает следующий неизбежный вывод:

strncpy() API ВСЕГДА напишите символы n в адрес, на который указывает буфер назначения.

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

На этом этапе вы должны спросить себя: «Так что же использовать?» Там есть , возможно, основной случай использования. Он позволяет копировать до n символы в целевой буфер с предсказуемостью, зная, что вы не превысите символы n . Период. В конечном счете, однако, вам нужна строка с нулевым завершением, поэтому это следующее:

char dst[ N ]; 
strncpy(dst, src, N-1);
dst[N-1] = 0;

где N является жесткой длиной буфера dst в символах и больше или равно 1 . Обратите внимание, что dst может быть как можно более удобным указателем на память:

char *dst = malloc( N * sizeof(char) ); 
strncpy(dst, src, N-1);
dst[N-1] = 0;

С вышесказанным вы always должны иметь строку с нулевым завершением в dst . Если исходная строка длина меньше указанной длины целевого буфера, strncpy() будет заполнять остальную часть буфера нулевыми символами до тех пор, пока общее количество символов-истоков -copied + tail-filled-null-characters равно n , а заключительный оператор является избыточным. Если длина исходной строки равна или больше , чем длина целевого буфера, strncpy() прекратит копирование после того, как будут достигнуты символы N-1 , а заключительный оператор устанавливает нулевой символ в конце буфера. Это приводит к префиксной строке «вырезания» исходного источника, но самое главное, это гарантирует, что вы НЕ превысите границы целевого буфера с более поздним вызовом строки-API, который сканирует терминатор.

Полезность вышеуказанного метода всегда спорна. Я парень C ++, поэтому std :: string сохраняет мое счастливое я от всего этого безумия. Но реальность такова: иногда вам не нравится, если src не скопирован в wholety на dst ; иногда вы этого не делаете. Полезность очень ситуационно зависима. Для представления строковых данных в пользовательском интерфейсе это не будет (вероятно). Для копирования строки, используемой для критических данных, частичная префикс-подстрока не будет приемлемой. Когда полиция выдаст ордер на арест «Джозефу Джонсону младшему», будет объяснение, когда его отец («Джозеф Джонсон») будет отправлен в тюрьму, потому что имя-буфер программного обеспечения для выдачи ордеров состоял только из 15 символов ,

Все это говорит о том, что ваша ошибка сегментации сводится к следующему утверждению:

strncpy(s1.from_str,src, 100);//length parameter is wrong.

Вспомните смелое утверждение выше: " strncpy() будет ВСЕГДА писать символы n в адрес, на который указывает буфер назначения." . Это означает, что приведенный выше код будет всегда записывать 100 символов в целевой буфер, который в вашем случае будет всего 10 символов, поэтому неопределенное поведение и вероятность ker-boom .

Исправьте это, выполнив следующее, если целевой буфер представляет собой массив символов фиксированной длины:

strncpy(s1.from_str,src, sizeof(s1.from_str)/sizeof(s1.from_str[0])-1);
s1.from_str[ sizeof(s1.from_str)/sizeof(s1.from_str[0])-1 ] = 0;

См. Предыдущее использование для того, как это сделать для динамической строки длины символов N.

13
добавлено
strncpy предназначен для подачи в поля фиксированного размера прогнозируемым образом. не «функция обработки строк».
добавлено автор vonbrand, источник
thnx @WhozCraig .. очень подробный и описательный .. Я просто обманывал strncpy, я знаю, как безопасно использовать strncpy. Я просто хотел узнать, является ли строка src NULL завершенной, то сколько символов копируется в строку «dest», если «n» больше, чем размер строки «src», а размер строки «dest» - достаточно большой, чтобы удерживать 'src', но меньше, чем 'n'. Описание страницы strncpy для MAN было недостаточно для меня.
добавлено автор Himanshu Gupta, источник

From http://www.cplusplus.com/reference/cstring/strncpy/

char * strncpy (char * destination, const char * source, size_t num   ); </Р>      

Копировать символы из строки Копирует первые несколько символов источника   к месту назначения. Если конец строки источника C (который сигнализируется   по нулевому символу) обнаруживается до того, как число символов будет скопировано,   пункт назначения заполняется нулями до тех пор, пока общее число символов не будет   был написан на нем.

Таким образом, хотя длина исходной строки меньше размера размера целевого буфера, однако из-за прохождения поведения strncpy она пытается наложить остальные символы за пределы размера целевого буфера, вызывая ошибку Сегментации

Вместо копирования более 100 символов размер должен быть равен максимально допустимому размеру в целевом буфере, поэтому вы могли бы написать

strncpy(s1.from_str,src,sizeof(s1.from_str)/sizeof(s1.from_str[0]) - 1); Actual size -1 to accomodate the null terminator 

или лучше написать макрос _countof

#define _countof(s) (sizeof(s)/sizeof(s[0]))
................
strncpy(s1.from_str,src,_countof(s1.from_str) - 1);
5
добавлено
@HimanshuGupta: Если ответ помог, попробуйте выполнить и принять ответ
добавлено автор Abhijit, источник
@HimanshuGupta: Запись за выделенную память - это неопределенное поведение (UB), а ошибка сегментации - это один из множества UB
добавлено автор Abhijit, источник
@HimanshuGupta: Что происходит под капотом, так это то, что strncpy пишет все, что приходит после того, как скопировано место. Что это может зависеть от переменных, которые вы определяете, как ваш компилятор выдает данные в памяти, если он удаляет некоторые переменные, потому что он знает, что они не используются (если это так, зависит от флагов оптимизации), ... и, наконец, на чем именно перезаписывается (обратный адрес?) и то, что написано в байтах, интерпретируется как (если обратный адрес, это может быть незаконный адрес инструкции, земля в середине глубокого do-do или быть безвредным).
добавлено автор vonbrand, источник
Эта ошибка seg происходит на одной машине LINUX, но ее не происходит на другой машине UNIX. Почему так ?
добавлено автор Himanshu Gupta, источник
thnx @Abhijit ..
добавлено автор Himanshu Gupta, источник

See: http://www.manpagez.com/man/3/strncpy/

Функции stpncpy() и strncpy() копируют не более n символов из s2      в s1. Если s2 меньше, чем n символов, оставшаяся часть s1 равна      заполнены символами `\ 0 '. В противном случае s1 не завершается.

Остальное заполнено ....

Так:

strncpy( s1.from_str, src, 10 );
1
добавлено
@icepack, потому что 90 байтов после того, как from_str (память не принадлежит вам, возможно), перезаписаны. И тогда это зависит от настроек/ОС компилятора. ... что происходит
добавлено автор Mario The Spoon, источник
так как это относится к вопросу? ..
добавлено автор SomeWittyUsername, источник
Это правильно, но в ответе не указывается ни одно из этого, это просто копия из ссылки
добавлено автор SomeWittyUsername, источник
Я просто обманываю. Это происходит на машине LINUX, но я не получаю никаких сбоев на другой машине UNIX.
добавлено автор Himanshu Gupta, источник
Thnx @icepack ..
добавлено автор Himanshu Gupta, источник
Thnx @Mario Ложка
добавлено автор Himanshu Gupta, источник
но здесь размер строки src меньше, чем у строки dest, а n больше, чем размер обеих строк src и dest.
добавлено автор Himanshu Gupta, источник

<�Код> strncpy (s1.from_str, SRC, 100);

Почему в вашей функции используется 100 , from_str и src имеют 10 последовательных байтов, но вы копируете 100 байт, что приводит к сегменту. неисправность.

используйте это,

<�Код> strncpy (s1.from_str, SRC, 10);

0
добавлено
Поскольку это приводит к неопределенному поведению , вы можете не получить отдельную ошибку на другой машине LINUX, но лучше использовать strcpy в вашем случае.
добавлено автор Adeel Ahmed, источник
Я просто обманываю. Это происходит на машине LINUX, но я не получаю никаких сбоев на другой машине UNIX.
добавлено автор Himanshu Gupta, источник
Thnx @ Adeel Ahmed
добавлено автор Himanshu Gupta, источник