As cadeias de caracteres são literais const?

Tanto o GCC quanto o Clang não reclamam se eu atribuir um literal de string a um char* , mesmo quando estiver usando muitas opções pedantes ( -Wall -W -pedantic -std=c99 ):

 char *foo = "bar"; 

enquanto eles (claro) se queixam se eu atribuir um const char* a um char* .

Isso significa que os literais de string são considerados do tipo char* ? Eles não deveriam ser const char* ? Não é comportamento definido se eles forem modificados!

E (uma questão não correlacionada) o que acontece com parâmetros de linha de comando (ie: argv ): é considerado um array de literais de string?

Eles são do tipo char[N] onde N é o número de caracteres, incluindo o \0 encerramento. Então, sim, você pode atribuí-los a char* , mas você ainda não pode escrever para eles (o efeito será indefinido).

Wrt argv : Aponta para uma matriz de pointers para strings. Essas cadeias são explicitamente modificáveis. Você pode alterá-los e eles são obrigados a manter o último valor armazenado.

Para completar o padrão de esboço C99 ( C89 e C11 têm texto similar ) na seção 6.4.5 Literais de seqüência, o parágrafo 5 diz:

[…] um byte ou código de valor zero é anexado a cada seqüência de caracteres multibyte que resulta de uma literal de string ou literais. A sequência de caracteres multibyte é então usada para inicializar uma matriz de duração e comprimento de armazenamento estáticos apenas o suficiente para conter a sequência. Para literais de cadeia de caracteres, os elementos da matriz têm o tipo char e são inicializados com os bytes individuais da seqüência de caracteres multibyte; […]

Então, isso diz que uma string literal tem duração de armazenamento estático ( dura a vida útil do programa ) e seu tipo é char[] (não char * ) e seu comprimento é o tamanho da string literal com um zero anexado. * Parágrafo 6 »diz:

Se o programa tentar modificar essa matriz, o comportamento é indefinido.

Portanto, tentar modificar um literal de string é um comportamento indefinido, independentemente do fato de que eles não são const .

Com relação ao argv na seção 5.1.2.2.1 , o parágrafo 2 da boot do programa diz:

Se forem declarados, os parâmetros para a function principal devem obedecer às seguintes restrições:

[…]

-Os parâmetros argc e argv e as strings apontadas pelo array argv devem ser modificáveis ​​pelo programa, e reter seus últimos valores armazenados entre a boot do programa e o término do programa.

Então argv não é considerado um array de literais de string e não há problema em modificar o conteúdo de argv .

Usando -Wwrite-strings opção -Wwrite-strings você terá:

 warning: initialization discards qualifiers from pointer target type 

Independentemente dessa opção, o GCC colocará literais na seção de memory somente leitura, a menos que seja dito o contrário usando -fwritable-strings (no entanto, esta opção foi removida das versões recentes do GCC).

parameters de linha de comando não são const, eles normalmente vivem na pilha.

(Desculpe, eu acabei de notar que essa questão é marcada como c , e não c++ . Talvez minha resposta não seja tão relevante para essa pergunta, afinal!)

Literais de string não são completamente const ou not-const , existe uma regra estranha especial para literais.

( Resumo : Literais podem ser tomados por referência a array como foo( const char (&)[N]) e não podem ser considerados como array não-const, eles preferem decair a const char * . parece que eles são const . Mas há uma regra legada especial que permite que os literais se decompõem em char * . Veja os experimentos abaixo.)

(Após os experimentos feitos no clang3.3 com -std=gnu++0x . Talvez este seja um problema do C ++ 11? Ou específico para o clang? De qualquer forma, há algo estranho acontecendo.)

Primeiramente, literais parece ser const :

 void foo( const char * ) { std::cout < < "const char *" << std::endl; } void foo( char * ) { std::cout << " char *" << std::endl; } int main() { const char arr_cc[3] = "hi"; char arr_c[3] = "hi"; foo(arr_cc); // const char * foo(arr_c); // char * foo("hi"); // const char * } 

Os dois arrays se comportam como esperado, demonstrando que foo é capaz de nos dizer se o ponteiro é const ou não. Então "hi" seleciona a versão const de foo . Então parece que isso resolve: literais são const ... não são?

Mas , se você remover void foo( const char * ) então fica estranho. Primeiro, a chamada para foo(arr_c) falha com um erro no tempo de compilation. Isso é esperado. Mas a chamada literal ( foo("hi") ) funciona através da chamada não-const.

Então, literais são "mais const" que arr_c (porque eles preferem decair para o const char * , diferente de arr_c . Mas literais são "menos const" do que arr_cc porque eles estão dispostos a decair para char * se necessário.

(Clang dá um aviso quando decai para char * ).

Mas e quanto ao decaimento? Vamos evitá-lo pela simplicidade.

Vamos pegar as matrizes por referência em foo. Isso nos dá resultados mais "intuitivos":

 void foo( const char (&)[3] ) { std::cout < < "const char (&)[3]" << std::endl; } void foo( char (&)[3] ) { std::cout << " char (&)[3]" << std::endl; } 

Como antes, o literal e o array const ( arr_cc ) usam a versão const, e a versão non-const é usada por arr_c . E se nós excluirmos foo( const char (&)[3] ) , então nós obtemos erros com ambos foo(arr_cc); e foo("hi"); . Em suma, se evitarmos o decaimento do ponteiro e usarmos a referência à matriz, os literais se comportarão como se fossem const .

Modelos?

Nos modelos, o sistema deduzirá const char * vez de char * e você ficará "preso" a isso.

 template void bar(T *t) { // will deduce const char when a literal is supplied foo(t); } 

Então, basicamente, um literal se comporta como const em todos os momentos, exceto no caso particular em que você inicializa diretamente um char * com um literal.

A resposta de Johannes está correta quanto ao tipo e conteúdo. Mas, além disso, sim, é um comportamento indefinido para modificar o conteúdo de um literal de string.

Sobre sua pergunta sobre o argv :

Os parâmetros argc e argv e as strings apontadas pela matriz argv devem ser modificáveis ​​pelo programa e reter seus últimos valores armazenados entre a boot do programa e a finalização do programa.

Eles são const char *, mas há uma exclusão específica para atribuí-los ao char * para o código legado que existia antes do const. E os argumentos da linha de comando definitivamente não são literais, eles são criados em tempo de execução.

Em C89 e C99, literais de string são do tipo char * (por razões históricas, como eu o entendo). Você está correto ao tentar modificar um resultado em comportamento indefinido. O GCC tem um sinalizador de aviso específico, -Wwrite-strings (que não faz parte de -Wall ), que avisará se você tentar fazer isso.

Quanto a argv , os argumentos são copiados no espaço de endereço do seu programa e podem ser modificados com segurança na function main() .

EDITAR : Whoops, tinha -Wno-write-strings copiados por acidente. Atualizado com o formulário correto (positivo) do sinalizador de aviso.

Os literais de string têm o tipo formal char [] mas o tipo semântico const char [] . Os puristas o odeiam, mas isso geralmente é útil e inofensivo, exceto por trazer muitos novatos para SO com “POR QUE O MEU PROGRAMA ESTÁ PISCANDO?!?!” questões.