2010-11-03 24 views
8

Possible en double:
strtok wont accept: char *strstrtok - tableau de caractères par rapport pointeur char

Lorsque vous utilisez la fonction strtok, en utilisant un char * au lieu d'un char [] se traduit par une erreur de segmentation.

Cela fonctionne correctement:

char string[] = "hello world"; 
char *result = strtok(string, " "); 

Cela provoque une erreur de segmentation:

char *string = "hello world"; 
char *result = strtok(string, " "); 

Quelqu'un peut-il expliquer les causes de cette différence de comportement?

Répondre

23
char string[] = "hello world"; 

Cette ligne initialise string être un tableau grand assez de caractères (dans ce cas char[12]). Il copie les caractères dans votre tableau local comme si vous aviez écrit sur

char string[] = { 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '\0' }; 

L'autre ligne:

char* string = "hello world"; 

n'initialise pas un tableau local, il initialise juste un pointeur local. Le compilateur est autorisé à le mettre à un pointeur sur un tableau qui vous n'êtes pas autorisé à changer, comme si le code était

const char literal_string[] = "hello world"; 
char* string = (char*) literal_string; 

La raison C permet cela sans un casting est principalement de laisser ancien code continuer la compilation. Vous devez prétendre que le type d'un littéral de chaîne dans votre code source est const char[], qui peut convertir en const char*, mais ne jamais le convertir en char*.

+0

Beaucoup de bonnes réponses, mais j'ai trouvé que c'était l'exemple le plus clair de la question fondamentale. –

+0

+1 excellente explication –

10

Dans le second exemple:

char *string = "hello world"; 
char *result = strtok(string, " "); 

le pointeur string pointe vers une chaîne littérale, qui ne peut être modifié (comme strtok() voudrait faire).

Vous pouvez faire quelque chose le long des lignes de:

char *string = strdup("hello world"); 
char *result = strtok(string, " "); 

de sorte que string pointe vers une copie modifiable du littéral.

+0

Je vais résister à l'envie de -1, mais je n'aime vraiment pas cette réponse. Je pense que cela amène les codeurs débutants à jeter un coup d'œil sur 'strdup' pour résoudre les erreurs de segmentation plutôt que d'apprendre comment gérer la mémoire (et en particulier les cordes). Mais je ne suis pas sûr de ce que serait une meilleure réponse sans dire "il suffit d'utiliser le tableau ou d'allouer dynamiquement de la mémoire pour votre chaîne". Soit dit en passant, 'strdup' n'est pas le standard C, mais bien sûr, il est assez facile à implémenter sur les systèmes qui ne l'ont pas. –

0

Le premier cas crée un tableau de caractères (non const) suffisamment important pour contenir la chaîne et l'initialiser avec le contenu de la chaîne. Le deuxième cas crée un pointeur char et l'initialise pour pointer sur la chaîne littérale, qui est probablement stockée dans la mémoire en lecture seule. Comme strtok veut modifier la mémoire pointée par l'argument que vous lui transmettez, le dernier cas provoque un comportement indéfini (vous passez un pointeur qui pointe sur un littéral (const)), donc il n'est pas surprenant qu'il plantages

3

Dans le deuxième cas (char *), la chaîne est en mémoire morte. Le correct type de constantes de chaîne est const char *, et si vous avez utilisé ce type pour déclarer la variable, vous serez averti par le compilateur lorsque vous avez essayé de le modifier. Pour des raisons historiques, vous êtes autorisé à utiliser des constantes de chaîne pour initialiser les variables de type char * même si elles ne peuvent pas être modifiées. (Certains compilateurs vous permettent de transformer cette licence historique au large, par exemple avec gcc -Wwrite-strings.)

+0

Il convient également de mentionner que, dans le premier cas, il existe une copie implicite de la chaîne littérale dans le tableau char. C'est pourquoi vous n'avez pas le même problème. –

+0

'const char *' aboutit également à une segfault si on essaie de l'utiliser avec 'strtok', mais au moins cela donne un avertissement de compilation. Mais noté à propos de la modification étant le problème. –

+0

Oui, j'aurais dû être moins télégraphique. Réponse éditée – zwol

0

Parce que le second déclare un pointeur (qui peut changer) à une chaîne constante ...

Donc, en fonction de votre compilateur/platform/OS/memory map ... la chaîne "hello world" sera stockée en tant que constante (dans un système embarqué, elle peut être stockée en ROM) et essayer de la modifier provoquera cette erreur.

4

strtok modifie la chaîne que vous lui passez (ou essaie de toute façon). Dans votre premier code, vous passez l'adresse d'un tableau qui a été initialisé à une valeur particulière - mais comme c'est un tableau normal de char, le modifier est autorisé.

Dans le deuxième code, vous transmettez l'adresse d'un littéral de chaîne.Tenter de modifier un littéral de chaîne donne un comportement indéfini.