strcpy, когда буфер dest меньше буфера src - PullRequest
5 голосов
/ 21 октября 2009

Я пытаюсь понять разницу / недостатки strcpy и strncpy. Может кто-нибудь, пожалуйста, помогите:

void main()
{
char src[] = "this is a long string";
char dest[5];

strcpy(dest,src) ;
printf("%s \n", dest);
printf("%s \n", src);

}

Вывод:

this is a long string 
a long string 

ВОПРОС: Я не понимаю, как изменился исходный код. В соответствии с объяснением, strcpy должен продолжать копировать, пока не встретит '\ 0', так оно и есть, но почему получилось изменить строку "src".

Пожалуйста, объясните.

Ответы [ 7 ]

11 голосов
/ 21 октября 2009

Простой ответ заключается в том, что вы (с помощью этого вызова strcpy ()) сделали что-то вне спецификаций системы и, таким образом, заслуженно страдали от неопределенного поведения.

Более сложный ответ заключается в изучении конкретной структуры памяти в вашей системе и о том, как strcpy () работает внутри. Вероятно, это выглядит примерно так:

     N+28 "g0PP"
     N+24 "trin"
     N+20 "ng s"
     N+16 "a lo"
     N+12 " is "
src  N+08 "this"
     N+04 "DPPP"
dest N+00 "DDDD"

Буквы D обозначают байты в dest, буквы P - это байты заполнения, символы 0 - это символы ASCII NUL, используемые в качестве ограничителей строки.

Теперь strcpy (dest, src) несколько изменит содержимое памяти (при условии, что оно правильно обрабатывает перекрывающиеся области памяти):

     N+28 "g0PP"
     N+24 "trin"
     N+20 "g0 s"
     N+16 "trin"
     N+12 "ng s"
src  N+08 "a lo"
     N+04 " is "
dest N+00 "this"

т.е. в то время как dest теперь «содержит» полную строку «это длинная строка» (если считать переполненную память), теперь src содержит совершенно другую NUL-оканчивающуюся строку «длинная строка».

7 голосов
/ 21 октября 2009

Это переполнение буфера и неопределенное поведение.

В вашем случае кажется, что компилятор поместил dest и src последовательно в память. Когда вы копируете из src в dest, оно продолжает копирование после конца dest и перезаписывает часть src.

1 голос
/ 21 октября 2009

В качестве дополнительного примечания имейте в виду, что функция strncpy не подходит для использования, когда вам необходимо выполнить копирование с защитой от переполнения буфера. Эта функция не предназначена для этой цели и никогда не предназначалась для этой цели. strncpy - это функция, которая была создана давным-давно для выполнения очень специфичного для приложения копирования строк в какой-то очень специфической файловой системе в некоторой старой версии UNIX. К сожалению, авторам библиотеки удалось «похитить» обобщенно звучащее имя strncpy, чтобы использовать его для этой очень узкой и конкретной цели. Затем он был сохранен для целей обратной совместимости. И теперь у нас есть поколение или два программиста, которые делают предположения о цели strncpy, основываясь исключительно на ее имени, и, следовательно, используют ее ненадлежащим образом. В действительности, strncpy имеет очень мало или вообще не имеет смысла.

Стандартная библиотека C (по крайней мере, ее версия C89 / 90) не имеет функции копирования строк с защитой от переполнения буфера. Чтобы выполнить такое защищенное копирование, вы должны использовать какую-либо платформо-зависимую функцию, например strlcpy , strcpy_s или написать ее самостоятельно.

P.S. Этот поток в StackOverflow содержит хорошее обсуждение реальной цели, для которой был разработан strncpy. См. post специально для точного объяснения его роли в файловой системе UNIX. Кроме того, см. здесь для хорошей статьи о том, как появился strncpy.

Еще раз, strncpy - это функция для копирования совершенно другого типа строки - строки фиксированной длины. Он даже не предназначен для использования с традиционными строками в стиле C с нулевым символом в конце.

1 голос
/ 21 октября 2009

Ваш код вызвал переполнение буфера - копирование в dest больше символов, чем он может содержать. Дополнительные символы были записаны в другом месте в стеке, в вашем случае, где src указывал на.

Вам нужно использовать функцию strncpy().

1 голос
/ 21 октября 2009

с высокой вероятностью строки являются точными соседями. Так что в вашем случае вы можете иметь эту картинку

дст | | | | | SRC | | | | | |

поэтому вы начинаете писать, и случается, что поля src перезаписываются.

Однако вы не можете на это положиться. Все может случиться, что у вас есть неопределенное поведение. Поэтому что-то еще может произойти на другом компьютере в другое время и / или в других вариантах.

С уважением Friedrich

0 голосов
/ 21 октября 2009

Либо я неправильно понимаю ваш вопрос, либо вы неправильно понимаете strcpy:

ВОПРОС: Я не понимаю, как Жало источника изменилось. Согласно объяснение, strcpy должен держать копирование до тех пор, пока он не встретит '\ 0', так это так, но как получилось "src 'string модифицировано.

Мне кажется, что вы ожидаете, что strcpy прекратит копировать в dest, когда он достигнет конца dest, основываясь на просмотре символа \0. Это не то, что он делает. strcpy копирует в место назначения, пока не достигнет конца строки источника, разделенного символом \0. Предполагается, что вы выделили достаточно памяти для копирования. Перед копией в буфере dest может быть что угодно, включая все нули.

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

0 голосов
/ 21 октября 2009

Я предлагаю краткое чтение:

http://en.wikipedia.org/wiki/Strncpy#strncpy

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

Теперь, когда вы используете strcpy для копирования одной строки поверх другой, он не проверяет результирующую область памяти, чтобы узнать, достаточно ли она велика - он не держит вас в этом отношении. Он проверяет нулевой символ в строке src.

Конечно, dst в этом примере составляет всего 5 байтов. Так что же происходит? Он продолжает писать, до конца Dest и далее мимо него в памяти. И в этом случае следующая часть памяти в стеке - ваша строка src. Таким образом, хотя ваш код не копирует его намеренно, это вызвано расположением байтов в памяти в сочетании с записью после конца dst.

Надеюсь, это поможет!

...