Разве strncpy не лучше strcpy?

Я знаю, что в Интернете есть много ответов, в которых говорится, что один лучше, чем другой, но я пришел к такому пониманию того, что strncpy() лучше с точки зрения предотвращения ненужных сбоев в системе из-за отсутствия '\0'. Как говорят многие, это довольно оборонительный подход.

Я уже переходил по этой ссылке: Почему вы должны использовать strncpy вместо strcpy ?

Скажем так:

char dest[5];
char src[7] = "Hello";
strncpy(dest, src, sizeof(dest)-1);

В этой реализации (ключ - sizeof(dest)-1, который всегда дает место для добавления '\0'), даже если в адресате есть усеченная строка, не всегда ли безопаснее использовать strncpy() вместо strcpy()? Другими словами, я пытаюсь понять, когда можно использовать strcpy() вместо strncpy()?


person haveri    schedule 19.04.2017    source источник
comment
Конечно, когда у человека есть полное представление об источнике и назначении. Сэкономьте несколько драгоценных циклов.   -  person StoryTeller - Unslander Monica    schedule 19.04.2017
comment
Неважно. std::string лучше.   -  person Captain Obvlious    schedule 19.04.2017
comment
... но @StoryTeller, поскольку это невозможно, пока код не был проверен (следовательно, есть ошибки, уб), защитное программирование требует всегда использовать strncpy.   -  person Paul Ogilvie    schedule 19.04.2017
comment
Что вы спрашиваете, что еще не решено в ответах на вопрос, который вы связали?   -  person John Bollinger    schedule 19.04.2017
comment
@PaulOgilvie Для защитного программирования требуется strlcpy   -  person Eugene Sh.    schedule 19.04.2017
comment
@PaulOgilvie - Что нужно доказывать при копировании c-строковых литералов в буферы известного размера !? :)   -  person StoryTeller - Unslander Monica    schedule 19.04.2017
comment
strncpy лучше, чем strcpy, но это все еще споры. Я предпочитаю использовать strncpy вместо strcpy.   -  person danglingpointer    schedule 19.04.2017
comment
@StoryTeller, некоторые увеличили строковый литерал, который теперь больше не умещается в буфере. (Вероятно, отдел маркетинга хотел получить более приятное сообщение или около того.)   -  person Paul Ogilvie    schedule 19.04.2017
comment
@PaulOgilvie - Наши полные знания изменились, но они все еще полны. Также существует проблема того, что строковый литерал является плохим выбором в качестве видимой для пользователя метки. Учитывая i18n, эти вещи в любом случае не должны быть жестко закодированы как литералы :)   -  person StoryTeller - Unslander Monica    schedule 19.04.2017
comment
Ваш код, как вы говорите, оставляет место для терминатора строки, но не помещает его, поэтому результирующая строка не заканчивается. Вы только что построили себе ловушку.   -  person Pete Becker    schedule 19.04.2017
comment
@PeteBecker комментарий является ключевым! OP исходил из ложного предположения, что фрагмент кода был безопасным (он сформировал строку), потому что использовался strncpy(), и вместо этого возникает проблема, когда код использует dest в качестве строки.   -  person chux - Reinstate Monica    schedule 19.04.2017
comment
@JohnBollinger: Чтобы понять, когда можно использовать strcpy вместо strncpy.   -  person haveri    schedule 19.04.2017
comment
Если у вас все в порядке с записью за пределами буфера, используйте strcpy. Если вам удобнее читать за пределами конца массива, используйте strncpy. Если вы хотите написать четко определенный код, используйте strncpy и явно завершите нулевой буфер целевым буфером.   -  person IInspectable    schedule 20.04.2017
comment
@PeteBecker: strncpy автоматически добавляет нулевой символ, если sizeof (dest) ›sizeof (src). Так что тогда dest будет адом.   -  person haveri    schedule 20.04.2017
comment
@anusha - RTFM. Эффект std::strncpy не зависит от размера целевого массива. Это зависит от того, сколько символов вы укажете для копирования, и если он достигнет этого предела до того, как увидит терминатор в исходном массиве, пункт назначения не получит терминатор. std::strncpy не является безопасной заменой std::strcpy согласно любому разумному определению слова "безопасно".   -  person Pete Becker    schedule 20.04.2017
comment
@PeteBecker: Может быть, это ты должен это сделать. Вся причина, по которой я использую sizeof (dest) -1, заключается в том, что strncpy добавляет символ NULL для этого 1 байта, который у меня все еще есть.   -  person haveri    schedule 20.04.2017
comment
@anusha - ОБРАТИТЕ ВНИМАНИЕ: ваш код не завершает строку результата. Он сообщает strncpy скопировать 4 символа, и поскольку strncpy не попадает в терминатор в исходной строке, он останавливается после четвертого символа без записи терминатора. Результатом копирования будет что-то вроде "Hell#(ALJLKJ", то есть в нем есть все случайные данные, находящиеся в памяти, начиная с dest[4], и любой код, использующий dest, с большой вероятностью получит доступ к памяти с конца массива. Это не закончится хорошо.   -  person Pete Becker    schedule 20.04.2017


Ответы (3)


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

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

Для достижения 1 простой цели требуется 2 строки, а разработчики легко забыть о второй.

Кроме того, он всегда записывает num символов (при необходимости добавляет 0) независимо от размеров строк, что может снизить производительность, когда dst маленький, а num большой.

Использование strncat устраняет вторую проблему с записью дополнительных символов, но по-прежнему заставляет вас писать две строки:

dst[0];
strncat(dst, src, num);

Функция strlcpy устраняет обе эти проблемы, но не стандартная библиотечная функция C, поэтому не используйте ее.

Правильный выбор - snprintf, который гарантированно завершает строку нулем и записывает только необходимые количество символов, даже если это означает усечение исходной строки.

snprintf(dst, size, "%s", src);
person imreal    schedule 19.04.2017
comment
Согласитесь, snprintf(dst, size, "%s", src); - лучшая альтернатива strncpy() для типичного использования для ограничения копирования. У него есть недостаток в обнаружении усеченных результатов, который является громоздким. (слишком долго, ошибка кодировки и изменение типа). - person chux - Reinstate Monica; 19.04.2017

В C strncpy имеет преимущество перед strcpy в том, что он не переполняет буфер, когда источник больше, чем место назначения.

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

strncpy(dst, src, n);
dst[n-1]='\0';

Пользователь Eugene Sh указывает, что для защитного программирования необходимо использовать strlcpy: «strlcpy () принимает полный размер целевого буфера и гарантирует завершение NUL».

person Paul Ogilvie    schedule 19.04.2017

В C ++ используйте std::string вместо строк в стиле c, если производительность в определенной области не является критической; в этом случае проверьте производительность, чтобы увидеть, является ли разница более чем незначительной. Вы можете использовать std::string::reserve для минимизации выделения, если заранее знаете размер.

На мой взгляд, 85% ошибок вызваны строками в стиле c. std::string намного безопаснее и менее подвержен ошибкам.

В C у вас не будет std::string доступных.

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

person Christopher Pisz    schedule 19.04.2017
comment
Конечно, нужно также использовать строки C при программировании на C, а не на C ++. - person John Bollinger; 19.04.2017
comment
My opinion cannot be inaccurate information, it is my opinion. Нет, это совсем не так. Сказать, что, на мой взгляд, вставить сюда случайную сфабрикованную статистику означает, что ваше мнение просто неточно. Независимо от того, мнение это или факт, оно выдумано и неверно. - person MD XF; 19.04.2017
comment
Чтобы ваш ответ был точным, вы можете перефразировать это так, чтобы сказать что-то вроде «В моем опыте, 85% ошибок ... или в моем коде, 85% ошибок. .. - person MD XF; 19.04.2017
comment
Для меня это звучит как наживка с огнеметом. Вы можете не соглашаться с моим мнением в комментариях. Вы не можете самостоятельно решить, что правда, а что нет, и отредактировать это в чьем-то ответе. Я предварял это, по моему мнению, я не вижу разницы между этим и своим опытом. Этого определенно нет в моем коде, поскольку я не использую строки в стиле c, если могу. Я просто исправляю код, изобилующий ошибками, в течение 20 лет. Публикация сканирования трекеров ошибок для 7 компаний (что я сделал), чтобы показать, что 85% ошибок происходят из строк в стиле c, здесь нецелесообразно. - person Christopher Pisz; 19.04.2017
comment
См. stackoverflow.com/help/editing - person Christopher Pisz; 19.04.2017
comment
strncpy не предотвращает переполнения. Он просто обменивается записью после конца с чтением после конца. Он не обнуляет пункт назначения во всех случаях, когда strcpy было бы проблематично. Вы должны хотя бы указать на эти опасности. - person IInspectable; 20.04.2017