Безопасно ли вводить строку, в которой нет места для нулевого терминатора?
Рассмотрим следующий код:
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
s всегда выделяются скрытым нулевым элементом. Если это так, то 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 символов.