Безопасно ли вводить строку, в которой нет места для нулевого терминатора?

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

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;

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

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


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

4 ответа

Решение

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

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

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

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

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

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

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

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

затем data() дает вам правильное \0строка:

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

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

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

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

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

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

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

Другие вопросы по тегам