A resposta é: depende de qual padrão C ++ você está compilando. Todo o código está perfeitamente bem formado em todos os padrões ‡ com exceção desta linha:
char * s = "My String";
Agora, a string literal tem tipo const char[10]
e estamos tentando inicializar um ponteiro não const para ela. Para todos os outros tipos, exceto a char
família de literais de string, essa inicialização sempre foi ilegal. Por exemplo:
const int arr[] = {1};
int *p = arr; // nope!
No entanto, no pré-C ++ 11, para literais de string, havia uma exceção em §4.2 / 2:
Um literal de string (2.13.4) que não é um literal de string largo pode ser convertido em um rvalue do tipo “ ponteiro para char ”; [...]. Em qualquer dos casos, o resultado é um ponteiro para o primeiro elemento da matriz. Essa conversão é considerada apenas quando há um tipo de alvo de ponteiro apropriado explícito e não quando há uma necessidade geral de converter de um lvalue em um rvalue. [Nota: esta conversão está obsoleta . Consulte o Anexo D. ]
Portanto, em C ++ 03, o código está perfeitamente bem (embora obsoleto) e tem um comportamento claro e previsível.
No C ++ 11, esse bloco não existe - não existe essa exceção para literais de string convertidos em char*
, e portanto o código é tão malformado quanto o int*
exemplo que acabei de fornecer. O compilador é obrigado a emitir um diagnóstico e, idealmente, em casos como este, que são violações claras do sistema de tipo C ++, esperaríamos que um bom compilador não apenas estivesse em conformidade a este respeito (por exemplo, emitindo um aviso), mas também falhasse completamente.
O código idealmente não deve ser compilado - mas sim no gcc e no clang (presumo porque provavelmente há muito código por aí que seria quebrado com pouco ganho, apesar desse tipo de falha no sistema estar obsoleto por mais de uma década). O código está malformado e, portanto, não faz sentido raciocinar sobre qual poderia ser o comportamento do código. Mas, considerando este caso específico e a história de ser permitido anteriormente, não acredito que seja um exagero irracional interpretar o código resultante como se fosse implícito const_cast
, algo como:
const int arr[] = {1};
int *p = const_cast<int*>(arr); // OK, technically
Com isso, o resto do programa está perfeitamente bem, já que você nunca s
mais tocará . Ler um const
objeto criado por meio de um não- const
ponteiro é perfeitamente normal. Escrever um const
objeto criado por meio de tal ponteiro é um comportamento indefinido:
std::cout << *p; // fine, prints 1
*p = 5; // will compile, but undefined behavior, which
// certainly qualifies as "unpredictable"
Como não há modificação em nenhum s
lugar em seu código, o programa funciona em C ++ 03, deve falhar ao compilar em C ++ 11, mas falha mesmo assim - e dado que os compiladores permitem, ainda não há um comportamento indefinido nele † . Com a permissão de que os compiladores ainda estão interpretando [incorretamente] as regras do C ++ 03, não vejo nada que possa levar a um comportamento "imprevisível". No s
entanto, escreva e todas as apostas serão canceladas. Em C ++ 03 e C ++ 11.
† Embora, novamente, por definição, código malformado não produza expectativa de comportamento razoável
‡ Exceto que não, veja a resposta de Matt McNabb