Почему вы должны использовать strncpy вместо strcpy?


84

Изменить: я добавил источник для примера.

Я наткнулся на такой пример :

Что произвело этот вывод:

пункт назначения изначально = 'abcdefg'
После strcpy пункт назначения становится '123456789'

destination1 изначально = 'abcdefg'
После strncpy destination1 становится '12345fg'

Это заставляет меня задуматься, зачем кому-то нужен этот эффект. Похоже, это сбивает с толку. Эта программа заставляет меня думать, что вы можете скопировать чье-то имя (например, Том Брокоу) с помощью Tom Bro763.

Какие преимущества использования strncpy() сверх strcpy() ?


81
Я думаю, вы хотели спросить: «Зачем кому-то использовать strcpyвместо strncpy
Сэм Харвелл,

5
Когда я был TA на первом семестре курса программирования на языке C, я заверил своих студентов, что любое использование подобных методов getlineприведет к неверным результатам, когда я оцениваю их по тщательно подобранным входным данным. :)
Сэм Харвелл

4
Я думаю, вы неправильно поняли, что на самом деле делает код. Присмотритесь.
Emil H

6
Очень жаль, что C так и не получил приличной стандартной библиотеки для строк.
starblue

7
это не ТАК жаль. Я имею в виду, это полностью сломало меня и сделало языки более высокого уровня НАМНОГО веселее :)
Carson Myers

Ответы:


98

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

Во-вторых, я не понимаю, почему вы решили копировать только 5 символов в строку из 7 символов, но это дает ожидаемое поведение. Это только копирование первых nсимволов, где nнаходится третий аргумент.

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


47
См. Lysator.liu.se/c/rat/d11.html : strncpyизначально был введен в библиотеку C для работы с полями имени фиксированной длины в таких структурах, как записи каталога. Такие поля не используются так же, как строки: конечный ноль не нужен для поля максимальной длины, а установка конечных байтов для более коротких имен на нуль обеспечивает эффективные полевые сравнения. strncpyпо своему происхождению не является «ограниченной strcpy», и Комитет предпочел признать существующую практику, а не изменять функцию, чтобы она лучше соответствовала такому использованию.
Sinan Ünür

35
Я не уверен, почему это набирает столько голосов - strncpy никогда не задумывался как более безопасная альтернатива strcpy и на самом деле не безопаснее, поскольку не обнуляет строку. Он также имеет различную функциональность в том смысле, что дополняет предоставленную длину символами NUL. Как сообщает caf в своем ответе - это для перезаписи строк в массиве фиксированного размера.
Dipstick

26
Факт остается фактом: strncpyэто не более безопасная версия strcpy.
Sinan Ünür

7
@Sinan: Я никогда не говорил, что так безопаснее. Это оборонительно. Это заставляет вас задуматься, следовательно, заставляет задуматься о том, что вы делаете. Есть лучшие решения, но факт остается фактом: люди будут (и будут) использовать strncpyвместо этого, strcpyпотому что это гораздо более защитная функция ... как я сказал.
Эрик

10
Все n функций используются для защиты от переполнения буфера. Пожалуйста, используйте их вместо старых функций, таких как strcpy. Это верно snprintf, но не имеет отношения strncatк strncpy. Как этот ответ мог получить столько голосов? Это показывает, насколько плоха ситуация с этой фиктивной функцией. Его использование не является защитным: в большинстве случаев программист не понимает его семантику и создает потенциально ненулевую завершающуюся строку.
chqrlie 05

180

В strncpy()Функция была разработана с очень конкретной проблемой в виде: манипуляциях строк , хранящихся в виде исходных записей каталога UNIX. Они использовали массив фиксированного размера, а нулевой символ конца использовался только в том случае, если имя файла было короче, чем массив.

Вот что стоит за двумя странностями strncpy():

  • Он не ставит нуль-терминатор на место назначения, если оно полностью заполнено; и
  • Он всегда полностью заполняет пункт назначения, при необходимости добавляя нуль.

Для «большей безопасности strcpy()» лучше использовать strncat()так:

Это всегда будет обнулять результат и не будет копировать больше, чем необходимо.


Но, конечно, strncpy - это не всегда то, что вам нужно: strncpy принимает максимальное количество добавляемых символов, а не размер целевого буфера ... Но это мелочь, поэтому, вероятно, не будет проблемой, если вы пытаемся соединить одну строку с другой.
Дэвид Волевер,

Я не знал причины этого, и это очень важно для банкомата, над которым я работаю.
Мэтт Джойнер

Функция strncpy () предназначена для хранения строк в формате фиксированной длины, дополненном нулями. Такой формат использовался для исходных записей каталога Unix, но также используется в бесчисленном множестве других мест, поскольку он позволяет хранить строку из 0-N байтов в N байтах памяти. Даже сегодня многие базы данных используют строки с нулевым заполнением в своих строковых полях фиксированной длины. Путаница с strncpy () проистекает из того факта, что она конвертирует строки в формат FLNP. Если нужна строка ЛНФ, это прекрасно. Если вам нужна строка с завершающим нулем, нужно обеспечить завершение самостоятельно.
supercat

1
зачем нам писать dest[0] = '\0';перед вызовом strncat? Не могли бы вы объяснить, сэр?
snr

4
@snr: strncat()объединяет исходную строку в конец строки назначения. Мы просто хотим скопировать исходную строку в место назначения, поэтому сначала устанавливаем в качестве места назначения пустую строку - вот что dest[0] = '\0';происходит.
caf

34

Хотя я знаю, что за strncpyэтим стоит, на самом деле это не очень хорошая функция. Избегайте обоих. Раймонд Чен объясняет .

Лично мой вывод состоит в том, чтобы просто избегать strncpyи всех его друзей, если вы имеете дело со строками с завершающим нулем. Несмотря на "str" ​​в имени, эти функции не создают строки с завершающим нулем. Они преобразуют строку с завершающим нулем в буфер необработанных символов. Использование их там, где ожидается строка с завершающим нулем, поскольку второй буфер явно неверен. Мало того, что вы не можете получить правильное завершение нулем, если источник слишком длинный, но если источник короткий, вы получите ненужное заполнение нулем.

См. Также Почему strncpy небезопасен?


27

strncpy НЕ безопаснее, чем strcpy, он просто заменяет один тип ошибок другим. В C при работе со строками C вам нужно знать размер ваших буферов, нет никакого способа обойти это. strncpy был оправдан для каталога, упомянутого другими, но в противном случае вы никогда не должны его использовать:

  • если вы знаете длину своей строки и буфера, зачем использовать strncpy? В лучшем случае это пустая трата вычислительной мощности (добавление бесполезного 0)
  • если вы не знаете длины, вы рискуете незаметно обрезать свои строки, что не намного лучше, чем переполнение буфера

Я думаю, что это хорошее описание strncpy, поэтому я проголосовал за него. У strncpy есть свои проблемы. Я думаю, это причина того, что, например, у glib есть собственные расширения. И да, очень жаль, что вы, как программист, должны знать размер всех массивов. Решение, содержащее завершенный 0 массив символов в виде строки, дорого обошлось нам ...
Фридрих,

1
Строки с нулями - довольно распространенное явление при хранении данных в файлах фиксированного формата. Безусловно, популярность таких вещей, как механизмы баз данных и XML, наряду с растущими ожиданиями пользователей, привели к тому, что файлы фиксированного формата стали менее распространенными, чем 20 лет назад. Тем не менее, такие файлы часто являются наиболее эффективным по времени средством хранения данных. За исключением случаев, когда существует огромное несоответствие между ожидаемой и максимальной длиной данных в записи, гораздо быстрее прочитать запись как отдельный фрагмент, содержащий некоторые неиспользуемые данные, чем прочитать запись, разделенную на несколько фрагментов.
supercat

Просто взял на себя обслуживание устаревшего кода, который использовал g_strlcpy (), поэтому не страдает неэффективностью заполнения, но, конечно же, количество переданных байтов НЕ поддерживалось, поэтому код молча усекал результат.
user2548100

21

То, что вы ищете, - это функция, strlcpy()которая всегда завершает строку с 0 и инициализирует буфер. Он также может обнаруживать переполнения. Единственная проблема, он (действительно) не переносимый и присутствует только в некоторых системах (BSD, Solaris). Проблема с этой функцией заключается в том, что она открывает еще одну банку червей, что видно из обсуждений на http://en.wikipedia.org/wiki/Strlcpy.

Лично я считаю, что это намного полезнее, чем strncpy()и strcpy(). Он имеет лучшую производительность и является хорошим компаньоном snprintf(). Для платформ, на которых его нет, это относительно легко реализовать. (на этапе разработки приложения я заменяю эти две функции ( snprintf()иstrlcpy() ) версией перехвата, которая жестко прерывает программу при переполнении или усечении буфера. Это позволяет быстро отлавливать худших нарушителей. Особенно, если вы работаете с кодовой базой от кого-то другого .

РЕДАКТИРОВАТЬ: strlcpy()можно легко реализовать:


3
Вы могли бы написать, что strlcpy доступен практически во всем, кроме Linux и Windows! Однако он лицензирован BSD, поэтому вы можете просто вставить его в одну из своих библиотек и использовать оттуда.
Michael van der Westhuizen

Вы можете добавить тест dstsize > 0и ничего не делать, если это не так.
chqrlie 05

Да, ты прав. Я добавлю проверку, так как без нее a dstsizeбудет запускать memcpyдлину lenв целевом буфере и переполнять его.
Патрик Шлютер

Плюс один за продвижение хороших решений. Больше людей должны знать о strlcpy, потому что все продолжают плохо его изобретать.
rsp

@MichaelvanderWesthuizen Он доступен в Linux, но не в glibc. См. Мои ответы для получения дополнительной информации (1) (2) (3)
rsp

3

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

Также для функций POSIX API, таких как read()which не помещает завершающий 0 в буфер, но возвращает количество прочитанных байтов, вы либо вручную поместите 0, либо скопируете его, используя strncpy().

В вашем примере кода на indexсамом деле это не индекс, а count- он сообщает, сколько символов не может быть больше копировать из источника в место назначения. Если среди первых n байтов источника нет нулевого байта, строка, помещенная в пункт назначения, не будет завершена нулем


1

strncpy заполняет место назначения с помощью '\ 0' для размера источника, хотя размер места назначения меньше ....

страница руководства:

Если длина src меньше n, strncpy () дополняет оставшуюся часть dest нулевыми байтами.

и не только остаток ... также после этого, пока не будет набрано n символов. И, таким образом, вы получаете переполнение ... (см. Реализацию страницы руководства)


3
strncpy заполняет место назначения с помощью '\ 0' для размера источника, даже несмотря на то, что размер места назначения меньше ... Боюсь, что это утверждение ошибочно и сбивает с толку: strncpyзаполняет место назначения с помощью '\ 0' для аргумент size, если длина источника меньше. Аргумент размера - это не размер источника, не максимальное количество символов, которое нужно скопировать из источника, поскольку он есть strncat, это размер места назначения.
chqrlie 05

@chqrlie: Совершенно верно. Преимущество strncpyперед другими операциями копирования заключается в том, что он гарантирует, что будет записан весь адрес назначения. Поскольку компиляторы могут попытаться проявить «творческий подход» при копировании структур, содержащих некоторые неопределенные значения, обеспечение полной записи любых символьных массивов внутри структур может быть самым простым способом предотвратить «сюрпризы».
supercat

@supercat: очень небольшое преимущество для этого конкретного случая ... но пункт назначения должен быть исправлен после вызова, strncpyчтобы гарантировать нулевое завершение: strncpy(dest, src, dest_size)[dest_size - 1] = '\0';
chqrlie

@chqrlie: потребуется ли завершающий нулевой байт, будет зависеть от того, что данные должны представлять. Использование данных с заполнением нулями вместо данных с завершающим нулем в структуре не так распространено, как раньше, но если, например, в формате объектного файла используются 8-байтовые имена разделов, возможность иметь char[8]внутри структуры обрабатывать вещи до 8 символов может быть лучше, чем использование, char[8]но возможность обрабатывать только 7 символов или необходимость копировать строку в char[9]буфер, а затем memcpyее в место назначения.
supercat

@chqrlie: большая часть кода, который делает что-то со строками, должна знать, какой длины они могут быть, и не должна слепо запускать charуказатели, пока они не достигнут нуля. В только вещи ноля байт действительно хороши как для строковых литералов, и даже там переменная длиной кодированного префикса, вероятно , будет лучше. Практически для всего остального было бы лучше иметь строки либо с префиксом длины, либо со специальным префиксом, который указывал бы, что char*это действительно что-то вроде struct stringInfo {char header[4]; char *realData; size_t length; size_t size;}.
supercat

-1

Это можно использовать во многих других сценариях, когда вам нужно скопировать только часть исходной строки в место назначения. Используя strncpy (), вы можете скопировать ограниченную часть исходной строки, в отличие от strcpy (). Я вижу, что код, который вы разместили, взят с сайта publib.boulder.ibm.com .


-1

Это зависит от наших требований. Для пользователей Windows

Мы используем strncpy всякий раз, когда мы не хотим копировать всю строку или хотим скопировать только n символов. Но strcpy копирует всю строку, включая завершающий нулевой символ.

Эти ссылки помогут вам больше узнать о strcpy и strncpy и о том, где мы можем их использовать.

о strcpy

о strncpy


-8

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


6
См. Lysator.liu.se/c/rat/d11.html : Функция strncpy strncpy изначально была введена в библиотеку C для работы с полями имени фиксированной длины в таких структурах, как записи каталога. Такие поля не используются так же, как строки: конечный ноль не нужен для поля максимальной длины, а установка конечных байтов для более коротких имен на нуль обеспечивает эффективные полевые сравнения. strncpy по происхождению не является `` ограниченным strcpy '', и Комитет предпочел признать существующую практику, а не изменять функцию, чтобы она лучше соответствовала такому использованию.
Sinan Ünür
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.