Différence entre char * str = “STRING” et char str = “STRING”?

En codant une fonction simple pour supprimer un caractère particulier d’une chaîne, je suis tombé sur cet étrange problème:

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[] = "ssortingng test"; // stack allocation? printf("Test: %s\n", test); str_remove_chars(test, ' '); // works printf("After: %s\n",test); } { char *test = "ssortingng test"; // non-writable? printf("Test: %s\n", test); str_remove_chars(test, ' '); // crash!! printf("After: %s\n",test); } return 0; } 

Ce que je ne comprends pas, c’est pourquoi le deuxième test échoue? Pour moi, cela ressemble à la première notation char *ptr = "ssortingng"; est équivalent à celui-ci: char ptr[] = "ssortingng"; .

N’est-ce pas le cas?

Les deux déclarations ne sont pas les mêmes.

char ptr[] = "ssortingng"; déclare un tableau de caractères de taille 7 et l’initialise avec les caractères
s , t , r , i , n , g et \0 . Vous êtes autorisé à modifier le contenu de ce tableau.

char *ptr = "ssortingng"; déclare ptr tant que pointeur de caractère et l’initialise avec l’adresse de la chaîne littérale "ssortingng" qui est en lecture seule . La modification d’un littéral de chaîne est un comportement indéfini . Ce que vous avez vu (erreur de segmentation) est une manifestation du comportement indéfini.

Ssortingctement parlant, une déclaration de char *ptr ne vous garantit qu’un pointeur sur le type de caractère. Il n’est pas rare que la chaîne fasse partie du segment de code de l’application compilée qui serait défini en lecture seule par certains systèmes d’exploitation. Le problème réside dans le fait que vous faites une supposition sur la nature de la chaîne prédéfinie (qu’elle est inscriptible) alors qu’en réalité, vous n’avez jamais explicitement créé de mémoire pour cette chaîne. Il est possible que certaines implémentations de compilateur et de système d’exploitation vous permettent de faire ce que vous avez tenté de faire.

Par ailleurs, la déclaration de char test[] , par définition, alloue réellement de la mémoire lisible et inscriptible pour tout le tableau de caractères de la stack dans ce cas.

char *test = "ssortingng test"; est faux, cela aurait dû être const char* . Ce code est compilé uniquement pour des raisons de compatibilité en amont. La mémoire désignée par const char* est une mémoire en lecture seule et chaque fois que vous essayez d’écrire, elle invoque un comportement indéfini. D’autre part, char test[] = "ssortingng test" crée un tableau de caractères accessible en écriture sur la stack. Ceci comme toute autre variable locale regualr dans laquelle vous pouvez écrire.

Autant que je m’en souvienne

 char ptr[] = "ssortingng"; 

crée une copie de "ssortingng" sur la stack, donc celle-ci est modifiable.

La forme

 char *ptr = "ssortingng"; 

est juste une compatibilité ascendante pour

 const char *ptr = "ssortingng"; 

et vous n’êtes pas autorisé (en termes de comportement indéfini) à modifier son contenu. Le compilateur peut placer ces chaînes dans une section de mémoire en lecture seule.

Bonne réponse @ codaddict.

En outre, un sizeof(ptr) donnera des résultats différents pour les différentes déclarations.

Le premier, la déclaration de tableau, renverra la longueur du tableau, y compris le caractère nul final.

Le second, char* ptr = "a long text..."; renverra la longueur d’un pointeur, généralement 4 ou 8.

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

est un code approprié et crée une chaîne mutable. Une mémoire est affectée à str, la valeur ‘test’ y étant renseignée.