char четыре[4] = четыре; Какова правильная семантика этого утверждения?

int main(void)
{
    char four[4] = "four";
    return 0;
}

При компиляции в виде программы C++ G++ сообщает

xxx.cpp: в функции int main():

xxx.cpp:3: ошибка: слишком длинная строка инициализатора для массива символов

При компиляции программы на C GCC не сообщает об ошибке.

Мне кажется, что присваивание правильно копирует все 4 байта в переменную, как я и ожидал.

Итак, мой вопрос сводится к.....

Правильно ли наблюдаемое поведение в C, или я где-то касаюсь неопределенного поведения, или это вообще что-то другое?


person EvilTeach    schedule 19.08.2010    source источник
comment
Существует очень похожий вопрос с ответами, которые соответствуют этому вопросу здесь: stackoverflow.com/questions/3216462/   -  person Johannes Schaub - litb    schedule 19.08.2010
comment
Поздравляю с находкой. Да, это немного обсуждает этот вопрос.   -  person EvilTeach    schedule 19.08.2010


Ответы (4)


Краткий ответ: ваш код действителен C, но недействителен C++.

Длинный ответ:

"four" на самом деле имеет длину 5 символов - для вас добавлено \0. В разделе 6.7.8 Инициализация, параграф 13, стандарт C говорит:

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

Таким образом, \0 просто игнорируется в вашей программе, когда она скомпилирована как C. C++ обрабатывает ее по-другому. На самом деле, этот конкретный случай явно вызывается в спецификации C++ (раздел 8.5.2 Символьные массивы, параграф 2):

Инициализаторов не должно быть больше, чем элементов массива. [ Пример:

char cv[4] = "asdf";  // error

имеет неправильный формат, так как нет места для подразумеваемого завершающего ’\0’. — конец примера ]

person Carl Norum    schedule 19.08.2010
comment
В C это допустимо, но должно выдать предупреждение на каком-то уровне. - person Joel; 19.08.2010
comment
@ Джоэл, я не думаю, что должно быть предупреждение, стандарт, похоже, указывает, что он полностью безопасен и четко определен. - person Carl Norum; 19.08.2010
comment
Хотя sizeof(four) равен 5 байтам, в переменную копируется только 4 байта. - person EvilTeach; 19.08.2010
comment
@Карл, можешь опубликовать раздел, на который ты ссылаешься, в качестве ответа? - person EvilTeach; 19.08.2010
comment
@Carl: Это совершенно законно, но часто является ошибкой и может привести к проблемам (например, strlen(four)). Стандарт не занимается определением того, что является абсолютно безопасным, он просто четко определен. Я хотел бы увидеть предупреждение. - person David Thornley; 19.08.2010
comment
@ Дэвид, я согласен на этот счет; Я должен был перефразировать свой комментарий. Я бы не удивился, если бы не было предупреждения. Быстрая проверка с помощью -Wall показывает, что на самом деле предупреждения нет, по крайней мере, в моей версии gcc. - person Carl Norum; 19.08.2010
comment
Это, безусловно, большой сюрприз, когда вы сталкиваетесь с этим при обновлении исходного кода C до C++. - person EvilTeach; 19.08.2010
comment
@EvilTeach, ну, я бы точно не назвал это обновлением. - person Carl Norum; 19.08.2010
comment
Я. Я думаю, что наиболее разумным было бы переписать char four[4] = {'f', 'o', 'u', 'r'}. Таким образом, о намерениях не может быть и речи. - person EvilTeach; 19.08.2010

Строка «четыре» на самом деле содержит пять байтов: четыре буквы плюс нулевой байт (\0) в качестве разделителя строки. Прошло некоторое время с тех пор, как я писал C или C++, но я предполагаю, что компилятор C молча игнорирует его по какой-то причине.

person fizban    schedule 19.08.2010

Лучше было бы

char four[] = "four";
person Jeff Walker    schedule 19.08.2010
comment
Что дает массив из пяти символов как в C, так и в C++, и отлично работает. - person David Thornley; 19.08.2010
comment
@David, только если вы хотите массив из пяти символов. Но если вам все равно, этот способ, безусловно, более ремонтопригоден. - person Carl Norum; 19.08.2010
comment
Правильно, я бы сказал, что вы почти никогда не захотите, чтобы char четыре[4] = четыре. - person Jeff Walker; 19.08.2010
comment
Я видел такие вещи довольно часто. Например, если вы имеете дело со структурами файловой системы или исполняемыми форматами, в разных местах файла часто присутствуют маркеры ASCII. Структура, которую вы используете для сопоставления с данными на диске, должна иметь одинаковую компоновку, поэтому для этих маркеров ASCII могут потребоваться массивы, не заканчивающиеся нулем, чтобы все имело смысл. В прошлом люди использовали многосимвольные литералы, такие как 'four', для обработки таких ситуаций, но в наши дни компилятор предупреждает об этом — использование массива кажется подходящей заменой. - person Carl Norum; 19.08.2010

То, что вы видите, — это разница между C и C++. C позволяет вам иметь дополнительные инициализаторы, которые игнорируются. C++ запрещает это: если вы указываете размер строки (или массива), он должен быть достаточно большим, чтобы вместить все инициализаторы (включая терминатор NUL в случае строки) или код имеет неправильный формат (стандартный для "это не разрешено - ожидайте, что компилятор отклонит его").

person Jerry Coffin    schedule 19.08.2010
comment
Нет. Я думаю, что дополнительный NUL рассматривается как особый случай. Если вы сделаете это char Four[4] = FiveX; Вы получаете ошибку в C. - person EvilTeach; 19.08.2010
comment
@EvilTeach - я получаю предупреждение, а не ошибку, в случае "fiveX". - person Carl Norum; 19.08.2010
comment
@EvilTeach (и Карл): Это (в основном) возвращается к одной трудности со стандартами: они требуют диагностики неправильного кода (но компилятор все равно может принять код, если он того пожелает), но компилятор должен определить что является (или не является) диагностикой. Также типично, что для соответствия необходимы определенные флаги, поэтому по умолчанию вы можете не получить даже этого. - person Jerry Coffin; 19.08.2010
comment
Кажется, он работает на моих платформах без какого-либо конкретного флага. Можете ли вы привести пример, когда код не работает или код не работает, если не установлен определенный флаг? - person EvilTeach; 20.08.2010
comment
@EvilTeach: дело не столько в том, что код не будет работать без определенных флагов, сколько в том, что без правильных флагов многие компиляторы (например) разрешат расширения, которые должны действительно быть помечены как ошибки. - person Jerry Coffin; 20.08.2010