2010-10-05 18 views
18

Bien que le codage d'une fonction simple pour supprimer un caractère particulier d'une chaîne, je suis tombé sur cette question étrange:Différence entre char * str = "STRING" et char str [] = "STRING"?

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; 
} 

Ce que je ne comprends pas pourquoi est le deuxième test échoue? Pour moi, il semble que la première notation char *ptr = "string"; est équivalente à celle-ci: char ptr[] = "string";.

N'est-ce pas le cas?

+0

Très bon article sur ce sujet: http://eli.thegreenplace.net/2009/10/21/are-pointers-and-arrays-equivalent-in-c/ – jyz

+0

Lire: [Différence entre 'char * str 'et' char str [] 'et comment les deux magasins en mémoire?] (http://stackoverflow.com/questions/15177420/what-does-sizeofarray-return/15177499#15177499) –

Répondre

37

Les deux déclarations ne sont pas identiques.

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

char *ptr = "string"; déclare ptr comme pointeur char et avec initialise adresse chaîne littérale"string" qui est en lecture seule. La modification d'un littéral de chaîne est un comportement non défini . Ce que vous avez vu (faute de seg) est une manifestation du comportement indéfini.

+0

Et un sizeof (ptr) donnera des résultats différents aussi pour les différentes déclarations. Le premier retournera la longueur du tableau incluant le caractère nul final. La seconde retournera la longueur d'un pointeur, généralement 4 ou 8. –

+0

Il est également vrai que le contenu de ptr peut être modifié. Mais le contenu est le pointeur vers le littéral, pas les caractères. – Darron

+3

+1, bonne réponse. Il est également vrai et important de comprendre qu'avec 'char * ptr =" string ";' le 'ptr' peut être pointé sur autre chose et peut donc être 'changé' dans ce qu'il pointe mais les caractères' "chaîne" 'est un littéral et ne peut pas changer. – dawg

5

Strictement parlant, une déclaration de char *ptr vous garantit uniquement un pointeur sur le type de caractère. Il n'est pas inhabituel que la chaîne fasse partie du segment de code de l'application compilée qui serait définie en lecture seule par certains systèmes d'exploitation. Le problème réside dans le fait que vous faites une hypothèse sur la nature de la chaîne prédéfinie (qu'elle est inscriptible) alors qu'en fait, vous n'avez jamais créé explicitement de mémoire pour cette chaîne vous-même. Il est possible que certaines implémentations du compilateur et du système d'exploitation vous permettent de faire ce que vous avez essayé de faire. D'autre part, la déclaration de char test[], par définition, alloue réellement une mémoire lisible et inscriptible pour l'ensemble du tableau de caractères de la pile dans ce cas.

2

Pour autant que je me souviens

char ptr[] = "string"; 

crée une copie de "string" sur la pile, de sorte que celui-ci est mutable.

La forme

char *ptr = "string"; 

est juste rétrocompatibilité pour

const char *ptr = "string"; 

et vous n'êtes pas autorisé (en termes de comportement non défini) pour modifier son contenu. Le compilateur peut placer de telles chaînes dans une section en lecture seule de la mémoire.

3

char *test = "string test"; est faux, il aurait dû être const char*. Ce code compile juste à cause des raisons de la rétrocompatibilité. La mémoire pointée par const char* est une mémoire en lecture seule et chaque fois que vous essayez d'y écrire, elle appelle un comportement indéfini.D'autre part, char test[] = "string test" crée un tableau de caractères inscriptible sur la pile. Ceci comme n'importe quelle autre variable locale regualr à laquelle vous pouvez écrire.

+0

Je n'irais pas jusqu'à dire que c'est faux. Vous pourriez avoir plus tard le point 'test' sur une chaîne modifiable, et garder un drapeau (dans une autre variable) indiquant s'il a été remplacé par quelque chose de modifiable. Cependant, dans la plupart des cas, c'est probablement une bonne pratique d'utiliser 'const'. –

0

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

La première, 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, habituellement 4 ou 8.

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

est le bon code et crée une chaîne mutable. str est affecté une mémoire dans le tas, la valeur 'test' est remplie.

+0

Hem. Ainsi, chaque fois que vous exécutez la fonction, vous allouez 5 octets? – Gui13

Questions connexes