Законно ли перезаписывать нулевой терминатор std::string?
В C++11 мы знаем, что std::string
гарантированно будет как непрерывным, так и нулевым (или, более педантично, завершенным charT()
который в случае char
нулевой символ 0).
Мне нужен этот C API, который заполняет строку указателем. Он записывает всю строку + нулевой терминатор. В C++03 я всегда был вынужден использовать vector<char>
потому что я не мог предположить, что string
был смежным или нулевым. Но в C++11 (при условии надлежащего соответствия basic_string
класс, который все еще сомнительный в некоторых стандартных библиотеках), я могу.
Или я могу? Когда я делаю это:
std::string str(length);
Строка будет выделять length+1
байтов, с последним заполненным нулевым терминатором. Это хорошо. Но когда я передаю это в C API, он собирается написать length+1
персонажи. Это собирается переписать нулевой терминатор.
Следует признать, что он будет перезаписывать нулевой терминатор нулевым символом. Хорошие шансы, что это сработает (действительно, я не представляю, как это не сработало).
Но мне все равно, что "работает". Я хочу знать, согласно спецификации, нормально ли перезаписывать нулевой терминатор нулевым символом?
4 ответа
LWG 2475 сделал это действительным, отредактировав спецификацию operator[](size())
(вставленный текст выделен жирным шрифтом):
В противном случае возвращает ссылку на объект типа
charT
со значениемcharT()
где изменение объекта на любое значение, кромеcharT()
приводит к неопределенному поведению.
К сожалению, это UB, если я правильно интерпретирую формулировку (в любом случае, это не разрешено):
§21.4.5 [string.access] p2
Возвращает:
*(begin() + pos)
еслиpos < size()
в противном случае ссылка на объект типаT
со значениемcharT()
; указанное значение не должно изменяться.
(Редакционная ошибка, которая говорит T
не charT
.)
.data()
а также .c_str()
в основном указывают на operator[]
(§21.4.7.1 [string.accessors] p1
):
Возвращает: указатель
p
такой, чтоp + i == &operator[](i)
для каждогоi
в[0,size()]
,
Согласно спецификации, перезаписывая завершающий NUL
должно быть неопределенное поведение. Таким образом, правильно было бы выделить length+1
символы в строке, передать строковый буфер в C API, а затем resize()
вернуться к length
:
// "+ 1" to make room for the terminating NUL for the C API
std::string str(length + 1);
// Call the C API passing &str[0] to safely write to the string buffer
...
// Resize back to length
str.resize(length);
(FWIW, я попробовал подход "перезаписи NUL" на MSVC10, и он отлично работает.)
Я полагаю, что n3092 больше не актуален, но это то, что у меня есть. Раздел 21.4.5 разрешает доступ к одному элементу. Требуется pos <= size(). Если pos Я думаю, что для языка программирования вид доступа, который может изменить значение, считается модификацией, даже если новое значение совпадает со старым значением. Есть ли в g++ педантичная библиотека, на которую вы можете ссылаться?