Pourquoi la conversion de la constante de chaîne en 'char *' est-elle valide en C mais non valide en C ++

163

La norme C ++ 11 (ISO / IEC 14882: 2011) dit dans § C.1.1:

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

Pour le C ++, c'est OK car un pointeur vers un littéral de chaîne est dangereux car toute tentative de le modifier conduit à un crash. Mais pourquoi est-ce valable en C?

Le C ++ 11 dit aussi:

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

Ce qui signifie que si un cast est ajouté à la première instruction, il devient valide.

Pourquoi le cast rend-il la deuxième instruction valide en C ++ et en quoi est-elle différente de la première? N'est-ce pas encore nocif? Si c'est le cas, pourquoi la norme dit-elle que c'est OK?

rullof
la source
3
C ++ 11 n'autorise pas le premier. Je n'ai aucune idée de la raison pour laquelle C a rendu le type d'une chaîne littérale char[]en premier lieu. Le second est un const_castdéguisé.
chris
4
Il y a tout simplement trop de code C hérité qui serait cassé si cette règle était modifiée.
Paul R
1
veuillez citer le texte là où la norme indique que le deuxième est OK.
Nawaz
13
Le langage C avait des littéraux de chaîne avant const, donc ils ne l'étaient pas nécessairement const.
Casey
2
C et C ++ vous permettent de convertir pratiquement n'importe quel type en un autre type. Cela ne signifie pas que ces moulages sont significatifs et sûrs.
Siyuan Ren

Réponses:

207

Jusqu'à C ++ 03, votre premier exemple était valide, mais utilisait une conversion implicite obsolète - un littéral de chaîne devrait être traité comme étant de type char const *, car vous ne pouvez pas modifier son contenu (sans provoquer un comportement indéfini).

Depuis C ++ 11, la conversion implicite qui était obsolète a été officiellement supprimée, donc le code qui en dépend (comme votre premier exemple) ne devrait plus être compilé.

Vous avez noté une façon d'autoriser la compilation du code: bien que la conversion implicite ait été supprimée, une conversion explicite fonctionne toujours, vous pouvez donc ajouter un cast. Je ne cependant considérer cette « fixation » le code.

Pour vraiment corriger le code, il faut changer le type du pointeur pour le type correct:

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

Quant à savoir pourquoi il était autorisé en C ++ (et l'est toujours en C): simplement parce qu'il y a beaucoup de code existant qui dépend de cette conversion implicite, et casser ce code (au moins sans avertissement officiel) a apparemment semblé aux comités standard comme Une mauvaise idée.

Jerry Coffin
la source
8
@rullof: C'est suffisamment dangereux pour ne pas donner de flexibilité significative, du moins pour le code qui se soucie (du tout) de la portabilité. L'écriture dans une chaîne littérale entraînera généralement l'abandon de votre programme sur un système d'exploitation moderne, donc autoriser le code à (essayer) d'y écrire n'ajoute aucune flexibilité significative.
Jerry Coffin le
3
Le code extrait donné dans cette réponse char const *p = "abc";est « valable et sûre dans les deux C et C ++ », pas « valide et en sécurité dans soit C ou C ++ ».
Daniel Le
4
@DanielLe ces deux phrases ont la même signification
Caleth
3
Oh mon dieu! [Insérez fermement la langue dans la joue] Désolé, mais "ou" est le terme correct ici. Le code peut être compilé en C ou en C ++, mais ne peut pas être compilé simultanément en C et C ++. Vous pouvez choisir l'un ou l'autre, mais vous devez faire un choix. Vous ne pouvez pas avoir les deux à la fois. [reprendre le fonctionnement normal de la langue].
Jerry Coffin
2
Non, à la fois / et est la formulation la plus claire et la plus correcte ici. Soit / ou arrive aussi à transmettre le bon sens, mais ce n'est pas aussi clair techniquement. Ou seul est irréfutablement faux ( A ou B n'est pas égal à A et B ).
Apollys soutient Monica le
15

Il est valide en C pour des raisons historiques. C spécifiait traditionnellement que le type d'un littéral de chaîne était char *plutôt que const char *, bien qu'il le qualifie en disant que vous n'êtes pas réellement autorisé à le modifier.

Lorsque vous utilisez un cast, vous dites essentiellement au compilateur que vous connaissez mieux que les règles de correspondance de type par défaut, et cela rend l'affectation OK.

Barmar
la source
3
C'était un char[N]et a été changé en const char[N]. Des informations de taille y sont attachées.
chris
1
Dans le type C de chaîne littérale est char[N]mais pas char*par exemple "abc"estchar[4]
Grijesh Chauhan
2

Vous pouvez également utiliser strdup :

char* p = strdup("abc");
baz
la source