Por que a conversão de string constante para 'char *' é válida em C, mas inválida em C ++

163

O padrão C ++ 11 (ISO / IEC 14882: 2011) diz § C.1.1:

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

Para o C ++, não há problema em apontar para um String Literal, pois qualquer tentativa de modificá-lo leva a uma falha. Mas por que isso é válido em C?

O C ++ 11 diz também:

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

O que significa que, se um elenco for adicionado à primeira instrução, ele se tornará válido.

Por que o elenco torna a segunda instrução válida em C ++ e como ela é diferente da primeira? Ainda não é prejudicial? Se for o caso, por que o padrão disse que está tudo bem?

rullof
fonte
3
O C ++ 11 não permite o primeiro. Não faço ideia por que C fez o tipo de uma string literal char[]em primeiro lugar. O segundo é um const_castdisfarçado.
chris
4
Há simplesmente muito código C legado que seria quebrado se essa regra fosse alterada.
Paul R
1
cite o texto onde o Padrão diz que o segundo é OK.
Nawaz
13
A linguagem C possuía literais de cadeias antes const, portanto não eram necessariamente const.
Casey #
2
C e C ++ permitem converter de praticamente qualquer tipo para outro tipo. Isso não significa que esses elencos sejam significativos e seguros.
Siyuan Ren

Respostas:

207

Até o C ++ 03, seu primeiro exemplo era válido, mas usava uma conversão implícita obsoleta - uma literal de cadeia de caracteres deve ser tratada como sendo do tipo char const *, pois você não pode modificar seu conteúdo (sem causar comportamento indefinido).

A partir do C ++ 11, a conversão implícita que havia sido descontinuada foi oficialmente removida; portanto, o código que depende dela (como o seu primeiro exemplo) não deve mais ser compilado.

Você observou uma maneira de permitir que o código seja compilado: embora a conversão implícita tenha sido removida, uma conversão explícita ainda funciona, para que você possa adicionar uma conversão. Eu não entanto, consideraria isso "corrigindo" o código.

Para corrigir o código, é necessário alterar o tipo do ponteiro para o tipo correto:

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

Por que isso foi permitido em C ++ (e ainda está em C): simplesmente porque há muito código existente que depende dessa conversão implícita, e a quebra desse código (pelo menos sem algum aviso oficial) aparentemente pareceu aos comitês padrão como Uma má ideia.

Jerry Coffin
fonte
8
@rullof: Já é perigoso o suficiente para não oferecer flexibilidade significativa, pelo menos para o código que se preocupa (em tudo) com a portabilidade. Gravar em um literal de string normalmente abortará seu programa em um sistema operacional moderno, portanto, permitir que o código (tente) escrever nele não adiciona flexibilidade significativa.
Jerry Coffin
3
O fragmento de código dado nesta resposta char const *p = "abc";é "válida e segura em ambos C e C ++", e não "válida e segura em qualquer C ou C ++".
Daniel Le
4
@DanielLe ambas as frases têm o mesmo significado
Caleth
3
Oh meu Deus! [Insira a língua firmemente na bochecha] Desculpe, mas "ou" é o termo correto aqui. O código pode ser compilado como C ou como C ++, mas não pode ser compilado simultaneamente como C e C ++. Você pode escolher qualquer um, mas deve fazer uma escolha. Você não pode ter os dois ao mesmo tempo. [retomar a operação normal da língua].
Jerry Coffin
2
Não, ambos / e é a redação mais clara e correta aqui. Acontece que / ou também transmite o significado certo, mas não é tão claro tecnicamente. Ou sozinho, é irrefutávelmente errado ( A ou B não é igual a A e B ).
Apollys suporta Monica 14/09
15

É válido em C por razões históricas. C tradicionalmente especificava que o tipo de uma string literal era, em char *vez deconst char * , embora a qualificasse dizendo que você não tem permissão para modificá-la.

Ao usar uma conversão, você está basicamente dizendo ao compilador que conhece melhor do que as regras de correspondência de tipo padrão e isso torna a atribuição correta.

Barmar
fonte
3
Foi um char[N]e foi alterado para const char[N]. Tem informações de tamanho anexadas a ele.
21414 chris
1
Em C tipo de literal string é char[N], mas não char*por exemplo, "abc"échar[4]
Grijesh Chauhan
2

Você também pode usar strdup :

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