¿Por qué la conversión de cadena constante a 'char *' es válida en C pero no válida en C ++

163

El estándar C ++ 11 (ISO / IEC 14882: 2011) dice en § C.1.1:

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

Para C ++ está bien, ya que un puntero a un String Literal es dañino ya que cualquier intento de modificarlo provoca un bloqueo. Pero, ¿por qué es válido en C?

El C ++ 11 también dice:

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

Lo que significa que si se agrega un reparto a la primera declaración, se vuelve válido.

¿Por qué la conversión hace que la segunda declaración sea válida en C ++ y en qué se diferencia de la primera? ¿Todavía no es dañino? Si es así, ¿por qué la norma dice que está bien?

rullof
fuente
3
C ++ 11 no permite el primero. No tengo idea de por qué C hizo literal el tipo de una cadena char[]en primer lugar. El segundo es un const_castdisfraz.
Chris
44
Simplemente hay demasiado código C heredado que se rompería si se cambiara esta regla.
Paul R
1
cite el texto donde el Estándar dice que es el segundo OK.
Nawaz
13
El lenguaje C tenía literales de cadena antes que los tenía const, por lo que necesariamente no lo eran const.
Casey
2
C y C ++ le permiten transmitir de casi cualquier tipo a otro tipo. Eso no significa que estos modelos sean significativos y seguros.
Siyuan Ren

Respuestas:

207

Hasta C ++ 03, su primer ejemplo fue válido, pero utilizó una conversión implícita en desuso: un literal de cadena debe tratarse como de tipo char const *, ya que no puede modificar su contenido (sin causar un comportamiento indefinido).

A partir de C ++ 11, la conversión implícita que había quedado en desuso se eliminó oficialmente, por lo que el código que depende de ella (como su primer ejemplo) ya no debería compilarse.

Ha notado una forma de permitir que el código se compile: aunque la conversión implícita se ha eliminado, una conversión explícita aún funciona, por lo que puede agregar un reparto. Yo no , sin embargo, considerar esta "fijar" el código.

La verdadera fijación del código requiere cambiar el tipo de puntero al tipo correcto:

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

En cuanto a por qué se permitió en C ++ (y todavía lo está en C): simplemente porque hay un montón de código existente que depende de esa conversión implícita, y romper el código (al menos sin alguna advertencia oficial) aparentemente pareció a los comités estándar como una mala idea.

Jerry Coffin
fuente
8
@rullof: es lo suficientemente peligroso como para no dar una flexibilidad significativa, al menos para el código que se preocupa (en absoluto) por la portabilidad. Escribir en un literal de cadena generalmente abortará su programa en un sistema operativo moderno, por lo que permitir que el código (intente) escribir allí no agregue ninguna flexibilidad significativa.
Jerry Coffin
3
El fragmento de código dado en esta respuesta char const *p = "abc";es "válida y segura en ambos C y C ++", no es "válida y segura en cualquiera de C o de C ++".
Daniel Le
44
@DanielLe ambas oraciones tienen el mismo significado
Caleth,
3
¡Oh mi señor! [Inserte la lengua firmemente en la mejilla] Lo siento, pero "o" es el término correcto aquí. El código se puede compilar como C o como C ++, pero no se puede compilar simultáneamente como C y C ++. Puede elegir cualquiera de los dos, pero debe elegir. No puedes tener ambos a la vez. [reanudar el funcionamiento normal de la lengua].
Jerry Coffin
2
No, ambos / y es la redacción más clara y correcta aquí. O / o también pasa a transmitir el significado correcto, pero técnicamente no es tan claro. O solo es irrefutablemente incorrecto ( A o B no es igual a A y B ).
Apollys apoya a Monica el
15

Es válido en C por razones históricas. C tradicionalmente especificaba que el tipo de literal de cadena era char *más que const char *, aunque lo calificaba diciendo que en realidad no se le permite modificarlo.

Cuando usa un elenco, básicamente le está diciendo al compilador que conoce mejor que las reglas de coincidencia de tipos predeterminadas, y hace que la asignación sea correcta.

Barmar
fuente
3
Fue un char[N]y fue cambiado a const char[N]. Tiene información de tamaño adjunta.
Chris
1
En C, el tipo de cadena literal es char[N]pero no es, char*por ejemplo, "abc"eschar[4]
Grijesh Chauhan
2

También puedes usar strdup :

char* p = strdup("abc");
baz
fuente