Чому конверсія з рядка є постійною в "char *", діє в C, але недійсна в C ++


163

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

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

Для C ++ це нормально, оскільки вказівник на Stral Literal є шкідливим, оскільки будь-яка спроба змінити його призводить до збоїв. Але чому він дійсний у С?

С ++ 11 також говорить:

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

Що означає, що якщо додаток до першого твердження додається, він стає дійсним.

Чому кастинг робить друге твердження дійсним у C ++ і чим він відрізняється від першого? Хіба це ще не шкідливо? Якщо це так, то чому стандарт сказав, що це нормально?


3
C ++ 11 не дозволяє перший. Я поняття не маю, чому С зробив тип рядка буквальним char[]в першу чергу. Другий - це const_castмаскування.
chris

4
Просто є занадто багато застарілого коду С, який би порушився, якби це правило було змінено.
Пол Р

1
будь ласка, цитуйте текст, де Стандарт говорить, що другий OK.
Наваз

13
Мова С до цього мала рядкові букви 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 обидва ці речення мають однакове значення
Калет

3
О мій пане! [Вставити язик щільно в щоку] Вибачте, але "чи" тут правильний термін. Код може бути складений як C, або як C ++, але не може бути одночасно компільований як C, так і C ++. Ви можете вибрати будь-який, але ви повинні зробити вибір. Ви не можете мати обох одразу. [відновити нормальну роботу язика].
Джері Коффін

2
Ні, і те / і є найяскравішим і найправильнішим формулюванням тут. Або / або також трапляється передати правильний зміст, але технічно це не так зрозуміло. Або поодинці безперечно помиляється ( А або В не дорівнює А і В ).
Аполліс підтримує Моніку

15

Це дійсно в С з історичних причин. C традиційно вказував, що тип рядкового літералу був, char *а не const char *, хоча він кваліфікував це, кажучи, що вам насправді не дозволяється змінювати його.

Коли ви використовуєте кастинг, ви, по суті, говорите компілятору, що ви знаєте краще, ніж правила відповідності типу за замовчуванням, і це робить призначення в порядку.


3
Це було char[N]і було змінено на const char[N]. До нього додається інформація про розмір.
chris

1
У C тип рядкового char[N]char*"abc"char[4]
буквалу

2

Ви також можете використовувати strdup :

char* p = strdup("abc");
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.