Почему преобразование из строковой константы в 'char *' допустимо в C, но недопустимо в C ++

163

Стандарт C ++ 11 (ISO / IEC 14882: 2011) гласит § C.1.1:

char* p = "abc"; // valid in C, invalid in C++

Для C ++ все нормально, так как указатель на строковый литерал вреден, поскольку любая попытка его изменить приводит к сбою. Но почему это действительно в C?

C ++ 11 также говорит:

char* p = (char*)"abc"; // OK: cast added

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

Почему приведение делает второй оператор действительным в C ++ и чем он отличается от первого? Разве это не вредно? Если это так, то почему стандарт сказал, что все в порядке?

rullof
источник
3
C ++ 11 не позволяет первый. Я понятия не имею, почему C сделал тип строки буквальным char[]во-первых. Второй const_castзамаскированный.
Крис
4
Просто слишком много устаревшего кода C, который сломался бы, если бы это правило было изменено.
Пол Р
1
пожалуйста, процитируйте текст, в котором говорится, что второй стандарт OK.
Наваз
13
В языке C раньше были строковые литералы const, поэтому их не обязательно было const.
Кейси
2
C и C ++ позволяют вам преобразовывать практически любой тип в другой тип. Это не означает, что эти броски являются значимыми и безопасными.
Сиюань Рен

Ответы:

207

До C ++ 03 ваш первый пример был верным, но в нем использовалось неявное неявное преобразование - строковый литерал должен рассматриваться как тип char const *, поскольку вы не можете изменять его содержимое (не вызывая неопределенного поведения).

Начиная с C ++ 11, неявное преобразование, которое было объявлено устаревшим, было официально удалено, поэтому код, который зависит от него (как ваш первый пример), больше не должен компилироваться.

Вы отметили один способ разрешить компиляции кода: хотя неявное преобразование было удалено, явное преобразование все еще работает, поэтому вы можете добавить приведение. Я бы не , однако, считаю это «фиксацию» кода.

Чтобы исправить код, нужно изменить тип указателя на правильный тип:

char const *p = "abc"; // valid and safe in either C or C++.

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

Джерри Гроб
источник
8
@rullof: Это достаточно опасно, что не дает сколько-нибудь значимой гибкости, по крайней мере для кода, который заботится (вообще) о переносимости. Запись в строковый литерал обычно приводит к прерыванию работы вашей программы в современной ОС, поэтому разрешение (попытка) записи кода не добавляет значимой гибкости.
Джерри Коффин
3
Фрагмент кода приведен в этом ответе char const *p = "abc";является «действительным и безопасно в обоих C и C ++», а не «действует и безопасно в любом C или C ++».
Даниэль Ле
4
@DanielLe оба эти предложения имеют одинаковое значение
Caleth
3
О Боже мой! [Вставьте язык твердо в щеку] Извините, но «или» является правильным термином здесь. Код может быть скомпилирован как C или C ++, но не может быть одновременно скомпилирован как C и C ++. Вы можете выбрать любой, но вы должны сделать выбор. Вы не можете иметь оба сразу. [возобновить нормальную работу языка].
Джерри Гроб
2
Нет, и / и это самая ясная и правильная формулировка здесь. Либо / или также случается передать правильное значение, но это не так ясно технически. Или одно неопровержимо неправильно ( A или B не равно A и B ).
Аполлис поддерживает Монику
15

Это действительно в C по историческим причинам. Традиционно C указывал, что тип строкового литерала был char *скорее, чем const char *, хотя он уточнил это, сказав, что вы на самом деле не можете изменять его.

Когда вы используете приведение, вы, по сути, говорите компилятору, что вы знаете лучше, чем правила сопоставления типов по умолчанию, и это делает назначение ОК.

Barmar
источник
3
Это было char[N]и было изменено на const char[N]. К нему прикреплена информация о размере.
Крис
1
В C тип строкового литерала есть, char[N]но не, char*например, "abc"естьchar[4]
Grijesh Chauhan
2

Вы также можете использовать strdup :

char* p = strdup("abc");
Baz
источник