2009-08-11 3 views
63

Редактировать: Я добавил источник для примера.Зачем использовать strncpy вместо strcpy?

я наткнулся на this example:

char source[MAX] = "123456789"; 
char source1[MAX] = "123456789"; 
char destination[MAX] = "abcdefg"; 
char destination1[MAX] = "abcdefg"; 
char *return_string; 
int index = 5; 

/* This is how strcpy works */ 
printf("destination is originally = '%s'\n", destination); 
return_string = strcpy(destination, source); 
printf("after strcpy, dest becomes '%s'\n\n", destination); 

/* This is how strncpy works */ 
printf("destination1 is originally = '%s'\n", destination1); 
return_string = strncpy(destination1, source1, index); 
printf("After strncpy, destination1 becomes '%s'\n", destination1); 

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

destination is originally = 'abcdefg' 
After strcpy, destination becomes '123456789' 

destination1 is originally = 'abcdefg' 
After strncpy, destination1 becomes '12345fg' 

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

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

+71

Я думаю, что вы имели в виду, чтобы спросить: "Почему на Земле будет кто-нибудь использовать' strcpy' вместо 'strncpy'?" –

+4

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

+4

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

ответ

76

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

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

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

+38

См. Http://www.lysator.liu.se/c/rat/d11.html: 'strncpy' был первоначально введен в библиотеку C для обработки полей имен фиксированной длины в таких структурах, как записи в каталоге. Такие поля не используются так же, как строки: конечный нуль не нужен для поля максимальной длины, а установка конечных байтов для более коротких имен с нулевым значением обеспечивает эффективные полевые сравнения. ** 'strncpy' по происхождению не является« ограниченным strcpy », и Комитет предпочел признать существующую практику, а не изменять функцию, которая лучше подходит для такого использования. ** –

+29

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

+6

@chris & Sinan: Он получает upvotes, потому что вопрос был: «Почему вы используете strncpy вместо strcpy?» Нет, «для чего это нужно?» Есть явное отличие. Этот ответ касается первого, а не последнего. – Eric

-8

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

+6

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

2

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

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

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

0

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

29

Хотя я знаю намерение позади strncpy, это не очень хорошая функция. Избегайте обоих. Raymond Chen explains.

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

Смотрите также Why is strncpy insecure?

136

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

Вот что за два странностей strncpy():

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

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

if (dest_size > 0) 
{ 
    dest[0] = '\0'; 
    strncat(dest, source, dest_size - 1); 
} 

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

+0

Но, конечно, strncpy не всегда то, что вы хотите: strncpy принимает максимальное количество символов, чтобы * добавить * и * не * размер буфера назначения ... Но это только незначительная вещь, поэтому, вероятно, не будет быть проблемой, если вы не пытаетесь объединить одну строку на другую. –

+0

Я не знал причину этого, и это очень актуально для того, что я работаю над atm. –

+0

Мне всегда было интересно, почему strncpy так странно. thx –

24

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

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

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

+0

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

+0

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

19

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

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

EDIT: strlcpy() может быть внедрено легко:

size_t strlcpy(char *dst, const char *src, size_t dstsize) 
{ 
    size_t len = strlen(src); 
    if(dstsize) { 
    size_t bl = (len < dstsize-1 ? len : dstsize-1); 
    ((char*)memcpy(dst, src, bl))[bl] = 0; 
    } 
    return len; 
} 
+3

Вы можете написать, что strlcpy доступен практически во всем, кроме Linux и Windows! Тем не менее, BSD лицензируется, поэтому вы можете просто поместить его в одну из своих библиотек и использовать его оттуда. –

+0

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

+0

Да, вы правы. Я добавлю чек, так как без него 'dstsize' вызовет' memcpy' длины 'len' в буфере назначения и переполнит его. –

2

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

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

Если длина SRC меньше, чем п, strncpy() колодки остаток Dest с нулевыми байтами.

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

+2

* strncpy заполняет цель до '\ 0' для размера источника, хотя размер места назначения меньше .... * Я боюсь, что это утверждение ошибочно и запутанно: 'strncpy' заполняет пункт назначения '\ 0' для аргумента размера, если длина источника меньше. Аргумент размера - это не размер источника, а не максимальное количество символов для копирования из источника, так как оно находится в 'strncat', это размер адресата. – chqrlie

+0

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

+0

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

0

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

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

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

about strcpy

about strncpy

 Смежные вопросы

  • Нет связанных вопросов^_^