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

Я освежаю свой C++ и наткнулся на любопытное поведение в отношении строк, массивов символов и нулевого символа ('\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
>

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


person ewok    schedule 20.07.2012    source источник
comment
Что происходит, когда вы cout << word2? Это может дать вам представление о том, что происходит.   -  person philipvr    schedule 20.07.2012
comment
@philipvr, печатающий все 3 представления, выдает просто строку hello.   -  person ewok    schedule 20.07.2012
comment
Если бы он был cout << word2, он бы распечатал hello, что на самом деле не проливает свет на его проблему.   -  person sean    schedule 20.07.2012


Ответы (4)


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

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

person sean    schedule 20.07.2012
comment
Кроме того, в случае string вы индексируете границы памяти string. Вам повезло, что ваш код не падает. - person Shahbaz; 20.07.2012
comment
В качестве примечания, если он действительно хочет скопировать всю строку, он может использовать std::string word3("hello\0there", 11); - person GWW; 20.07.2012

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

Итак, ваш последний пример фактически вызывает неопределенное поведение; word2[6] идет дальше конца строки.

person Oliver Charlesworth    schedule 20.07.2012

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

person RedX    schedule 20.07.2012

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

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*» или «string», и символы NULL не имеют к этому никакого отношения: вы просто печатаете 6-й символ слова, а 6-й символ слова2.

Если я правильно понимаю ваше намерение, ваш тест должен выглядеть так:

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

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

person Gerasimos R    schedule 19.10.2015