La respuesta es: depende del estándar C ++ con el que compile. Todo el código está perfectamente formado en todos los estándares ‡ con la excepción de esta línea:
char * s = "My String";
Ahora, el literal de cadena tiene tipo const char[10]
y estamos tratando de inicializar un puntero que no sea constante. Para todos los demás tipos distintos de la char
familia de cadenas literales, dicha inicialización siempre fue ilegal. Por ejemplo:
const int arr[] = {1};
int *p = arr; // nope!
Sin embargo, en pre-C ++ 11, para cadenas literales, hubo una excepción en §4.2 / 2:
Un literal de cadena (2.13.4) que no es un literal de cadena ancha se puede convertir a un rvalue de tipo " puntero a char "; [...]. En cualquier caso, el resultado es un puntero al primer elemento de la matriz. Esta conversión se considera solo cuando hay un tipo de destino de puntero apropiado explícito, y no cuando existe una necesidad general de convertir de un lvalue a un rvalue. [Nota: esta conversión está obsoleta . Ver Anexo D. ]
Entonces, en C ++ 03, el código está perfectamente bien (aunque en desuso) y tiene un comportamiento claro y predecible.
En C ++ 11, ese bloque no existe; no existe tal excepción para los literales de cadena convertidos a char*
, por lo que el código está tan mal formado como el int*
ejemplo que acabo de proporcionar. El compilador está obligado a emitir un diagnóstico, e idealmente en casos como este que son claras violaciones del sistema de tipos C ++, esperaríamos que un buen compilador no solo se ajuste a este respecto (por ejemplo, emitiendo una advertencia) sino que falle. total.
Idealmente, el código no debería compilarse, pero lo hace tanto en gcc como en clang (supongo que porque probablemente hay mucho código que se rompería con poca ganancia, a pesar de que este tipo de agujero del sistema ha estado en desuso durante más de una década). El código está mal formado y, por lo tanto, no tiene sentido razonar sobre cuál podría ser el comportamiento del código. Pero considerando este caso específico y la historia de que se permitió previamente, no creo que sea un tramo irrazonable interpretar el código resultante como si fuera un implícito const_cast
, algo como:
const int arr[] = {1};
int *p = const_cast<int*>(arr); // OK, technically
Con eso, el resto del programa está perfectamente bien, ya que nunca volverás a tocar s
. Leer un const
objeto creado a través de un no const
puntero está perfectamente bien. Escribir un const
objeto creado a través de dicho puntero es un comportamiento indefinido:
std::cout << *p; // fine, prints 1
*p = 5; // will compile, but undefined behavior, which
// certainly qualifies as "unpredictable"
Como no hay modificaciones en s
ninguna parte de su código, el programa está bien en C ++ 03, debería fallar al compilar en C ++ 11 pero lo hace de todos modos, y dado que los compiladores lo permiten, todavía no hay un comportamiento indefinido en él † . Teniendo en cuenta que los compiladores siguen interpretando [incorrectamente] las reglas de C ++ 03, no veo nada que pueda conducir a un comportamiento "impredecible". Sin s
embargo, escriba a y todas las apuestas están canceladas. Tanto en C ++ 03 como en C ++ 11.
† Aunque, nuevamente, por definición, el código mal formado no genera expectativas de comportamiento razonable
‡ Excepto que no, vea la respuesta de Matt McNabb