#c #c #strncpy
#c #c #strncpy
Вопрос:
По следующему поводу strncpy
: http://www.cplusplus.com/reference/clibrary/cstrin&/strncpy /, в нем упоминается следующее:
Нулевой символ неявно добавляется в конец destination , поэтому destination будет заканчиваться нулем только в том случае, если длина строки C в source меньше num.
Что подразумевается под этим предложением?
Комментарии:
1. Короче говоря: избегайте использования
strncpy
в качестве безопасногоstrcpy
, это другая функция для другой цели, которая подвержена другим рискам безопасности.2. Это помечено C намеренно? Если вы используете C , я бы полностью избегал строковых функций C и использовал функции C Std::Strin& . В противном случае я бы удалил тег C …
3. Я не согласен с удалением тега, @James, хотя и не согласен с вашим советом использовать «правильные» строки. Однако, хотя в подавляющем большинстве случаев, вероятно, было бы лучше использовать строки C , все еще существуют ситуации, когда старые строки C могут быть полезны, и, в конце концов, они являются частью стандарта C .
4. cplusplus.com это сайт, полный плохо сформулированной, вводящей в заблуждение и часто просто неверной информации. Я бы избегал этого и вместо этого прочитал соответствующие документы по стандартам.
5. Я поддерживаю рекомендацию для cplusplus.com . Для всего, что на C, предпочитайте результаты поиска из open&roup.or& для cplusplus.com . Сравнивая их в этом примере, cplusplus.com забывает упомянуть, что поведение не определено для перекрывающихся источника / назначения. Текст Open&roup взят из Posix и более или менее воспроизводит стандарт C. Для C трудно найти хорошую информацию в виде веб-страницы, поэтому выполните поиск в Интернете по названиям нужных вам классов / функций, а затем найдите их в книге. Или PDF стандарта.
Ответ №1:
Это означает, что, например, если ваша исходная строка состоит из 20 символов плюс нулевой ограничитель, а ваша strncpy
указывает менее 21 символа, к целевой строке не будет добавлено значение null.
Это из-за того, как это работает: strncpy
гарантирует, что он запишет ровно N байт, где N — переданное значение длины.
Если длина исходной строки (без нулевого байта) меньше указанной, то область назначения будет заполнена нулями. Если оно равно или больше, вы не получите значение null, добавленное к месту назначения.
Это означает, что технически это может не быть строка C, которую вы получаете. Это можно решить с помощью кода, подобного:
char d[11]; // Have enou&h room for strin& and null.
strncpy (d, s, 10); // Copy up to 10 bytes of strin&, null the rest.
d[10] = ''; // Then append null manually in case s was too lon&.
Вы выделяете 11 байт (индексы массива 0..10), копируете до 10 (индексы 0..9), затем присваиваете 11-му (индекс 10) значение null.
Вот диаграмма, показывающая три возможности записи строк различного размера в область из 10 символов, strncpy (d, s, 10)
где .
представлен нулевой байт:
s d
------------- ----------
Hello. Hello.....
Hello Fred. Hello Fred
Hello Geor&e. Hello Geor
Обратите внимание, что во втором и третьем случаях нулевой байт не записан, поэтому, если вы будете обрабатывать d
как строку, вы, вероятно, будете разочарованы результатом.
Комментарии:
1. «вы, вероятно, будете разочарованы результатом» — да, почему UB всегда — это тупые назальные демоны, и никогда, например, торт? То есть не носовой пирог.
2. @Steve: ха-ха, хороший вопрос. На самом деле, это может быть основой для целого вопроса (хотя, даже будучи CW, он, вероятно, был бы закрыт): «Кто-нибудь когда-нибудь сталкивался с неопределенным поведением, которое было хорошим? Нет, не только то, что он сделал то, что вы ожидали, но и то, что он вышел за рамки call of duty.»
3. @Steve, боюсь, что даже когда UB создает cake, он, как правило, со вкусом демона
4. @bdonlan: или с назальным привкусом.
5. @paxdiablo. Спасибо за ваш ответ. Не могли бы вы просто объяснить эту строку «d[10] = »;»? Особенно то, что когда «s» слишком длинное, место «10» уже будет заполнено? Как я могу тогда добавить «null»? Спасибо.
Ответ №2:
Строка "foo"
состоит из 3 символов 1 нулевого ограничителя (она хранится как "foo"
), что дает общую длину 4. Если вы вызываете strncpy
с n=3
(или меньшим количеством), он не добавит нулевой ограничитель в конец целевой строки, а только скопирует "foo"
. Попытка напечатать результирующую строку приведет к неопределенному поведению из-за отсутствия нулевого ограничителя, который сигнализирует о конце строки.
Вы должны быть очень осторожны с этим и либо передать n
единицу, превышающую максимальный исходный код, либо добавить нулевой ограничитель самостоятельно.
Ответ №3:
Это означает, что он копирует завершающий null исходной строки, но не добавляет завершающий null, если исходная строка не вписывается в место назначения.
Ответ №4:
В C строки хранятся в виде массивов char
‘s и заканчиваются нулем, что означает, что в конце к ним добавлено дополнительное 0
значение, которое обозначает конец строки и может быть использовано позже для определения длины строки. Итак, строка "hello"
выглядит в памяти следующим образом:
char hello[] = {'h', 'e', 'l', 'l', 'o', 0};
Обычно, когда вы копируете строку, null
символ также должен быть скопирован. Таким образом, объем памяти, необходимый для строкового буфера, равен его длине 1 (например, (strlen(hello) 1) * sizeof(char)
).
Функция strncpy
позволяет копировать только столько символов, сколько возможно вместить в предоставленный буфер. В случае, если предоставленный вами буфер недостаточно велик для хранения этого дополнительного null
, он не будет добавлен. Или, если строка обрезана, она не будет заканчиваться нулем.
char hello[] = "hello"; // 5 characters, 6 bytes lon&
char hel[3];
strncpy(hel, hello, 3); // hel is {'h', 'e', 'l'}
Вы всегда должны быть осторожны после вызова strncpy
, поскольку результатом может быть недопустимая строка C . Если строка не завершается нулем, невозможно узнать ее длину, и большинство функций обработки строк завершатся сбоем или сделают что-то неожиданное.
Комментарии:
1. Пожалуйста, никогда не
* sizeof(char)
, это сокращает срок службы вашей клавиатуры из-за дополнительных нажатий клавиш, а вашего монитора — из-за необходимости дополнительных электронов 🙂2. Хорошо, возможно, так и должно было быть
sizeof(hello[0])
.
Ответ №5:
Это означает, что из исходного буфера в буфер назначения будут скопированы только num
байты; поэтому, если длина исходной строки больше или равна num
, завершающий нулевой байт копироваться не будет, и результат не будет иметь завершающий нулевой байт, что опасно.
Вместо этого рекомендуется использовать strlcpy.
Комментарии:
1. Рекомендовано кем? Конечно, не о блоках &libc 🙂 Моя рекомендация — изучить все недостатки используемых вами инструментов, а не полагаться на костыли. Люди, которые хотят такого уровня безопасности, должны покинуть прекрасную страну C и вернуться к Visual Basic 🙂 И, пожалуйста, обратите внимание на смайлики! Я не могу сказать вам, сколько аргументов вызвало здесь мое сухое чувство юмора 🙂
Ответ №6:
Семантика strncpy()
, даже если она точно объяснена так, как она приведена в приведенной выше ссылке на C , часто понимается неправильно. Поведение этой функции противоречит здравому смыслу и подвержено ошибкам.
Чтобы избежать проблем при его использовании или в дальнейшем процессе разработки, когда сопровождающий неправильно прочтет код и добавит еще больше мелких ошибок, есть простое решение: НИКОГДА НЕ ИСПОЛЬЗУЙТЕ ЭТУ ФУНКЦИЮ.
Более подробную информацию об этом вы можете прочитать в этой статье Брюса Доусона.
Чтобы ответить на ваш вопрос: если исходная строка длиннее размера, переданного в качестве третьего аргумента (обычно соответствующего размеру буфера назначения), функция скопирует символы размера в пункт назначения, и среди них не будет нулевого байта. Затем вызов strlen(destination);
вызовет неопределенное поведение, поскольку он будет пытаться прочитать дальше конца массива, пока не найдет нулевой ограничитель. Именно это специфическое поведение делает strncpy
настолько подверженным ошибкам.