Использование нулевого символа в строках (С++)

Я очищаю свой С++ и наткнулся на любопытное поведение в отношении строк, массивов символов и нулевого символа ('\0'). Следующий код:

#include <iostream>
using namespace std;

int main() {
    cout << "hello\0there"[6] << endl;

    char word [] = "hello\0there";
    cout << word[6] << endl;

    string word2 = "hello\0there";
    cout << word2[6] << endl;

    return 0;
}

выводит результат:

> t
> t
>

Что происходит за кулисами? Почему строковый литерал и объявленный массив char хранят 't' в индексе 6 (после внутреннего '\0'), но объявленная строка не работает?

Ответ 1

Из того, что я помню, первые два по существу представляют собой только массив, а способ печати строки - продолжать печатать до тех пор, пока не встретится \0. Таким образом, в первых двух примерах вы начинаете с смещения точки 6-го символа в строке, но в вашем случае вы печатаете 6-й символ, который равен t.

Что происходит с классом string, так это то, что он копирует строку в свой собственный внутренний буфер и делает это, копируя строку с начала массива до первого найденного \0. Таким образом, t не сохраняется, потому что он приходит после первого \0.

Ответ 2

Поскольку конструктор std::string, который принимает const char*, рассматривает свой аргумент как строку стиля C. Он просто копирует его, пока он не попадет в нуль-терминатор, а затем прекратит копирование.

Таким образом, ваш последний пример действительно вызывает поведение undefined; word2[6] проходит мимо конца строки.

Ответ 3

Вы создаете строку из char* (или что-то, что затухает). Это означает, что применяется соглашение для C-строк. То есть они '\0' завершены. Поэтому word2 содержит только "hello".

Ответ 4

Проблема в том, что вы вообще не печатаете строки - вы печатаете одиночные символы.

char word [] = "hello\0there";//Array of char...
cout << word[6] << endl;      //So word[6] is the char't' (NOT a string)

string word2 = "hello\0there"; //std::string...
cout << word2[6] << endl;      //so word2[6] is the char 't' (NOT a string as well)

Итак, вы вызываете перегрузки "char", а не "char *" или "строковые" перегрузки вообще, а символы NULL не имеют к этому никакого отношения: вы просто печатаете 6-й символ слова и 6-й символ слова2.

Если я правильно читаю ваши намерения, ваш тест должен выглядеть следующим образом:

cout << &(word[6]) (char*, should print "there")
cout << &(word2[6]) (char* as well, undefined behaviour pre-C++11)

В С++ 11 и более поздних версиях также будет напечатано "there" И будет четко определено