Безопасно ли вводить строку, в которой нет места для нулевого терминатора? - PullRequest
0 голосов
/ 10 января 2019

Рассмотрим следующий код:

const char foo[] = "lorem ipsum"; // foo is an array of 12 characters
const auto length = strlen(foo); // length is 11
string bar(length, '\0'); // bar was constructed with string(11, '\0')

strncpy(data(bar), foo, length);
cout << data(bar) << endl;

Насколько я понимаю, string всегда имеют скрытый нулевой элемент. Если это так, то bar действительно выделяет 12 символов, причем 12 th является скрытым '\0', и это совершенно безопасно ... Если я ошибаюсь, тогда cout приведет к неопределенному поведению, потому что нет нулевого терминатора.

Может ли кто-нибудь подтвердить для меня? Это законно?


Было много вопросов о том, почему использовать strncpy вместо простого использования string(const char*, const size_t) конструктора. Моим намерением было сделать мой игрушечный код близким к моему фактическому коду, который содержит vsnprintf. К сожалению, даже после получения отличных ответов, я обнаружил, что vsnprintf не ведет себя так же, как strncpy, и задал следующий вопрос: Почему vsnprintf не записывает одинаковое количество символов как бы strncpy?

Ответы [ 4 ]

0 голосов
/ 10 января 2019

Это безопасно, если вы копируете [0, size()) символов в строку. За [basic.string] / 3

Во всех случаях [data(), data() + size()] является допустимым диапазоном, data() + size() указывает на объект со значением charT() («нулевой терминатор»), а size() <= capacity() равно true.

Итак, string bar(length, '\0') дает вам строку с size(), равным 11, с неизменяемым нулевым терминатором в конце (всего 12 символов в реальном размере). Пока вы не перезаписываете этот нулевой терминатор и не пытаетесь писать после него, все в порядке.

0 голосов
/ 10 января 2019

Здесь есть две разные вещи.

Во-первых, добавляет ли strncpy дополнительный \0 в этом случае (11 не-\0 элементов, которые нужно скопировать в строку размером 11). Ответ нет:

Копирует самое большее количество символов строки байтов, на которые указывает src (включая завершающий нулевой символ), в массив символов, на который указывает dest.

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

Так что звонок в порядке.

Тогда data() дает вам правильную \0 -определенную строку:

c_str () и data () выполняют одну и ту же функцию. (начиная с C ++ 11)

Похоже, что для C ++ 11 вы в безопасности. Независимо от того, выделяет ли строка дополнительные \0 или нет, в документации не указано, но API ясно, что то, что вы делаете, прекрасно.

0 голосов
/ 10 января 2019

Вы выделили 11 символов std::string. Вы не пытаетесь ни читать, ни писать что-либо после этого, так что эта часть будет в безопасности.

Таким образом, реальный вопрос заключается в том, испортили ли вы внутреннюю часть строки. Поскольку вы не сделали ничего, что не разрешено, как это возможно? Если для строки требуется внутреннее хранение 12-байтового буфера с нулевым заполнением в конце, чтобы выполнить свой контракт, это будет иметь место независимо от того, какие операции вы выполняли.

0 голосов
/ 10 января 2019

Да, это безопасно в соответствии с char * strncpy (char * destination, const char * source, size_t num) :

Копировать символы из строки

Копирует первые num символов источника в место назначения. Если конец исходной строки C (который обозначен нулевым символом) найден до того, как будет скопировано num символов, пункт назначения заполняется нулями, пока в него не будет записано всего num символов.

...