Почему массивы символов с отдельными символами не заканчиваются нулем-терминатором, в отличие от строковых литералов?

Я играл с массивами символов в С++ и написал эту программу:

int main()
{

char text[] = { 'h', 'e', 'l', 'l', 'o' };  //arrays initialised like this 
                                            //will have a size of the number 
                                            //of elements that you see

char text2[] = "hello"; //arrays initialised like this will have a size of 
                        //the number of elements that you see + 1 (0 on the 
                        //end to show where the end is

cout << endl;

cout << "The size of the first array is: " << sizeof(text) << endl;

cout << endl;

for (int i = 0; i < sizeof(text); i++)
{
    cout << i << ":" << text[i] << endl;
}
cout << endl;

cout << "The size of the first array is: " << sizeof(text2) << endl;

cout << endl;

for (int i = 0; i < sizeof(text2); i++)
{
    cout << i << ":" << text2[i] << endl;
}
cout << endl;

cin.get();

return 0;
}

Эта программа дает мне вывод:

The size of the first array is: 5

0:h
1:e
2:l
3:l
4:o

The size of the first array is: 6

0:h
1:e
2:l
3:l
4:o
5:

Мой вопрос: есть ли конкретная причина, по которой инициализация массива символов с отдельными символами не будет иметь нулевой терминатор (0) на конце, в отличие от инициализации массива символов строковым литералом?


person Chris Gray    schedule 06.04.2018    source источник
comment
было бы довольно раздражающе, если бы каждый массив char имел неявно добавленный нуль, в то время как для строковых литералов это именно то, что вы хотите   -  person 463035818_is_not_a_number    schedule 06.04.2018
comment
Просто так работает язык. Когда вы берете под свой контроль и указываете, что вы хотите ({ 'h', 'e', 'l', 'l', 'o' }), это то, что вы получаете.   -  person NathanOliver    schedule 06.04.2018
comment
Хорошее наблюдение! Я предполагаю, что ответ таков: что, если мне действительно нужен массив char, который не является строкой? Как я мог получить это иначе?   -  person BoBTFish    schedule 06.04.2018
comment
см. также stackoverflow.com/a/17943529/1132334   -  person Cee McSharpface    schedule 06.04.2018
comment
Потому что иногда вам нужен массив байтов вместо символов? Это действительно зависит от варианта использования, поэтому компилятор не может делать никаких предположений.   -  person Some programmer dude    schedule 06.04.2018
comment
возможно, вас смущает то, что не каждый массив char используется для хранения последовательностей символов. char — это просто тип, подобный int или float, который может содержать некоторые значения. Использование в качестве строки — это всего лишь один вариант использования, хотя и очень распространенный.   -  person 463035818_is_not_a_number    schedule 06.04.2018
comment
Странный дубликат, который заметило сообщество, не так ли? Это не упоминало явный массив символов.   -  person Bathsheba    schedule 06.04.2018
comment
@Bathsheba Ответ ответил, хотя, возможно, и не так прямо, как вам хотелось бы: stackoverflow.com/a/40821770/2757035   -  person underscore_d    schedule 06.04.2018
comment
@underscore_d: Странная политика. Я мог бы создать вопрос, что такое стандарт C++, ответить на него дословной копией стандарта C++ и закрыть каждый вопрос C++ для этого ответа. Для меня дубликат должен быть вопросом, который является точной копией этого вопроса. Диск дешевый.   -  person Bathsheba    schedule 06.04.2018


Ответы (6)


Инициализатор фигурных скобок просто предоставляет указанные значения для массива (или, если массив больше, остальные элементы используются по умолчанию). Это не строка, даже если элементы являются значениями char. char — это просто наименьший целочисленный тип.

Строковый литерал обозначает завершающуюся нулем последовательность значений.

Это все.

person Cheers and hth. - Alf    schedule 06.04.2018
comment
s/целочисленный тип со знаком/целочисленный тип, так как мы не знаем, какова подписанность char. - person NathanOliver; 06.04.2018
comment
На всякий случай Ура и чт. - Альф сейчас в пабе, я сделал дерзкую правку. - person Bathsheba; 06.04.2018
comment
Хорошо, это имеет смысл, спасибо всем за комментарии/ответы. - person Chris Gray; 06.04.2018

Неформально это второй символ кавычек в строковом литерале формы "foo", который добавляет NUL-терминатор.

В C++ "foo" — это тип const char[4], который затухает до const char* в определенных ситуациях.

Просто язык работает, вот и все. И это очень полезно, поскольку прекрасно сочетается со всеми стандартными библиотечными функциями, которые моделируют строку как указатель на первый элемент в массиве chars, оканчивающемся NUL.

Добавление дополнительного элемента с чем-то вроде char text[] = { 'h', 'e', 'l', 'l', 'o' }; было бы действительно раздражающим и могло бы внести несогласованность в язык. Сделали бы вы то же самое, например, для signed char и unsigned char? А как же int8_t?

person Bathsheba    schedule 06.04.2018

Есть ли конкретная причина, по которой инициализация массива символов с отдельными символами не будет иметь нулевой терминатор (0)

Причина в том, что этот синтаксис...

Type name[] = { comma separated list };

...используется для инициализации массивов любого типа. Не только char.

Синтаксис "quoted string" является сокращением для очень специфического типа массива, который предполагает нулевой терминатор.

person Drew Dormann    schedule 06.04.2018

Когда вы указываете набор соседних символов, разделенных двойными кавычками (строковый литерал), предполагается, что вам нужна строка. И строка в C означает массив символов, заканчивающийся нулем, потому что это то, что ожидают функции, работающие со строками (printf, strcpy и т. д.). Таким образом, компилятор автоматически добавляет для вас этот нулевой терминатор.

Когда вы предоставляете разделенный скобками список разделенных запятыми символов, разделенных одинарными кавычками, предполагается, что вам не нужна строка, а нужен массив точных символов, которые вы указали. Таким образом, нулевой терминатор не добавляется.

C++ наследует это поведение.

person Benjamin Lindley    schedule 06.04.2018
comment
Обратите внимание, что в C "foo" является типом char[4], хотя пытаться изменить его - UB. Также обратите внимание, что 'h' является типом int в C. Другими словами, языки настолько сильно расходятся в этой области, что я избегал сравнения. - person Bathsheba; 06.04.2018

Строковый литерал, такой как, например, этот "hello", имеет тип константного массива символов и инициализируется следующим образом.

const char string_literal_hello[] = { 'h', 'e', 'l', 'l', 'o', '\0' };

Как видно, тип строкового литерала — const char[6]. Он содержит шесть символов.

Таким образом, эта декларация

char text2[] = "hello"; 

это также можно записать как

char text2[] = { "hello" }; 

на самом деле заменяется следующим объявлением

char text2[] = { 'h', 'e', 'l', 'l', 'o', '\0' };

То есть строковый литерал используется в качестве инициализатора массива символов, все его символы используются для инициализации массива.

person Vlad from Moscow    schedule 06.04.2018

Вы можете прекратить его самостоятельно несколькими способами:

char text1[6] = { 'h', 'e', 'l', 'l', 'o' };
char text2[sizeof "hello"] = { 'h', 'e', 'l', 'l', 'o' };
char text3[] = "hello"; // <--- my personal favourite
person Maxim Egorushkin    schedule 06.04.2018