Концы std::strings заканчиваются на '\0' при инициализации строковым литералом?
Я знаю, что строковые объекты не заканчиваются нулем, но почему это должно работать?
std::string S("Hey");
for(int i = 0; S[i] != '\0'; ++i)
std::cout << S[i];
Таким образом, конструктор также копирует нулевой терминатор, но не увеличивает длину? Почему это беспокоит?
3 ответа
Таким образом, конструктор также копирует нулевой терминатор, но не увеличивает длину?
Как вы знаете, что std::string
не содержит нулевой символ (и здесь не копируется нулевой символ).
Дело в том, что вы используете std::basic_string::operator[]
, Согласно C++11 std::basic_string::operator[] будет возвращать нулевой символ, когда указанный индекс эквивалентен size()
,
Если
pos == size()
, ссылка на символ со значениемCharT()
(нулевой символ) возвращается.Для первой (неконстантной) версии поведение не определено, если этот символ изменен на любое значение, кроме
charT()
,
std::string
хранит свои данные внутри себя в виде C-строки с нулевым символом в конце, но при обычном использовании не позволяет получить доступ к нулевому терминатору.
Например, если я назначу значение "Hello, World!" для строки внутренний буфер будет выглядеть так:
std::string myString("Hello, World!");
// Internal Buffer...
// [ H | e | l | l | o | , | | W | o | r | d | ! | \0 ]
// ^ Null terminator.
В этом примере нулевой терминатор НЕ был скопирован с конца строкового литерала, но был добавлен внутри std::string
,
Как @songyuanyao упоминает в своем ответе, результатом этого является то, что myString[myString.size()];
возвращается '\0'
,
Так почему же std::string
назначить нулевой терминатор в конце строки? Это, конечно, не должно поддерживать один, потому что вы можете добавить '\0'
в строку, и она включена в строку:
std::string myString;
myString.size(); // 0
myString.push_back('\0');
myString.size(); // 1
Причиной такого поведения является поддержка std::string::c_str()
функция. c_str()
функция требуется для возврата с нулевым символом в конце const char *
, Самый эффективный способ сделать это - просто вернуть указатель на внутренний буфер, но для этого во внутреннем буфере должен быть нулевой символ-терминатор в конце строки. Начиная с C++11, строки должны включать нулевой терминатор для поддержки этого.
PS Хотя это и не является частью вашего вопроса, следует отметить, что цикл из вашего вопроса НЕ может возвращать полную строку, если ваша строка содержит нулевые символы:
std::string S("Hey");
S.push_back('\0');
S.append("Jude");
for(int i = 0; S[i] != '\0'; ++i)
std::cout << S[i];
// Only "Hey" is printed!
Перегрузка: класс имеет специальный способ хранения своей длины, поэтому ему не требуется нулевое завершение, как строкам в стиле C. Вы можете использовать
S[i]
для прямого доступа к символам, и цикл работает без'\0'
.Эффективное обращение:
std::string
Конструктор не добавляет нулевой терминатор при создании строки, поскольку он использует более разумный метод управления длиной. Этот подход более эффективен для обработки и манипулирования строками.