Diferença entre char * str = “STRING” e char str = “STRING”?

Enquanto codificava uma function simples para remover um caractere particular de uma string, eu me deparei com esse estranho problema:

void str_remove_chars( char *str, char to_remove) { if(str && to_remove) { char *ptr = str; char *cur = str; while(*ptr != '\0') { if(*ptr != to_remove) { if(ptr != cur) { cur[0] = ptr[0]; } cur++; } ptr++; } cur[0] = '\0'; } } int main() { setbuf(stdout, NULL); { char test[] = "string test"; // stack allocation? printf("Test: %s\n", test); str_remove_chars(test, ' '); // works printf("After: %s\n",test); } { char *test = "string test"; // non-writable? printf("Test: %s\n", test); str_remove_chars(test, ' '); // crash!! printf("After: %s\n",test); } return 0; } 

O que eu não entendo é porque o segundo teste falha? Para mim, parece a primeira notação char *ptr = "string"; é equivalente a este: char ptr[] = "string"; .

Não é o caso?

As duas declarações não são as mesmas.

char ptr[] = "string"; declara uma matriz char de tamanho 7 e inicializa com os caracteres
s , t , r , i , n , g e \0 . Você tem permissão para modificar o conteúdo desta matriz.

char *ptr = "string"; declara ptr como um ponteiro char e o inicializa com o endereço da string literal "string" que é somente leitura . Modificar um literal de string é um comportamento indefinido . O que você viu (falha de seg) é uma manifestação do comportamento indefinido.

Estritamente falando, uma declaração de char *ptr apenas lhe garante um ponteiro para o tipo de caractere. Não é incomum que a cadeia faça parte do segmento de código do aplicativo compilado que seria definido como somente leitura por alguns sistemas operacionais. O problema está no fato de que você está fazendo uma suposição sobre a natureza da string pré-definida (que é gravável) quando, na verdade, você nunca criou explicitamente memory para essa string. É possível que algumas implementações do compilador e do sistema operacional permitam que você faça o que você tentou fazer.

Por outro lado, a declaração de char test[] , por definição, aloca memory legível e gravável para toda a matriz de caracteres na pilha, neste caso.

char *test = "string test"; está errado, deveria ter sido const char* . Esse código é compilado apenas por motivos de compatibilidade com versões anteriores. A memory apontada por const char* é uma memory somente de leitura e sempre que você tentar escrever nela, ela invocará um comportamento indefinido. Por outro lado, char test[] = "string test" cria uma matriz de caracteres graváveis na pilha. Isso como qualquer outra variável local regualr na qual você possa escrever.

Até onde eu lembro

 char ptr[] = "string"; 

cria uma cópia de "string" na pilha, então esta é mutável.

A forma

 char *ptr = "string"; 

é apenas compatibilidade com versões anteriores

 const char *ptr = "string"; 

e você não tem permissão (em termos de comportamento indefinido) para modificar seu conteúdo. O compilador pode colocar essas cadeias em uma seção somente leitura da memory.

Boa resposta @codaddict.

Além disso, um sizeof(ptr) fornecerá resultados diferentes para as diferentes declarações.

O primeiro, a declaração da matriz, retornará o comprimento da matriz, incluindo o caractere nulo de terminação.

O segundo, char* ptr = "a long text..."; irá retornar o comprimento de um ponteiro, geralmente 4 ou 8.

 char *str = strdup("test"); str[0] = 'r'; 

é o código correto e cria uma string mutável. str é atribuída uma memory no heap, o valor ‘teste’ preenchido.