2009-03-05 8 views
4

J'ai le code suivant:Pourquoi cette simple affectation de chaîne segfault?

#include <iostream> 
using namespace std; 
int main() 
{ 
    char* a = "foo"; 
    char* b = "bar"; 
    a = b; 
    cout << a << ", " << b << endl; 
    return 0; 
} 

Ceci compile et fonctionne, par exemple. imprime bar,bar. Maintenant, je voudrais démontrer que ce qui se passe ici ne copie pas une chaîne. Je voudrais changer b et montrer que a change également. Je suis venu avec ce code simple:

#include <iostream> 
using namespace std; 
int main() 
{ 
    char* a = "foo"; 
    char* b = "bar"; 
    a = b; 
    b[1] = 'u'; // ← just this line added 
    cout << a << ", " << b << endl; 
    return 0; 
} 

... mais il se sépare. Pourquoi? La chose intéressante est que la modification suivante fonctionne très bien:

#include <iostream> 
using namespace std; 
int main() 
{ 
    char* a = "foo"; 
    char b[] = "bar"; // ← declaration changed here 
    a = b; 
    b[1] = 'u'; 
    cout << a << ", " << b << endl; 
    return 0; 
} 

Pourquoi ne pas segfault comme le précédent? Je suppose qu'il me manque une différence importante entre le style pointeur et l'initialisation de chaîne de style tableau.

Répondre

9

Vous ne pouvez pas modifier les constantes de chaîne, ce qui est ce que vous obtenez lorsque vous utilisez la syntaxe pointeur vers littéral comme dans les premiers exemples de code.

Voir aussi cette question: Is a string literal in c++ created in static memory?.

+0

Notez également que votre code donne des avertissements du compilateur qui expliquent ce problème: « Avertissement: conversion dépréciée de constante chaîne « char * » » – schnaader

+0

g ++ -Wall -pedantic 4.01 n'a pas, que dois-je allumer pour obtenir l'avertissement? – zoul

+0

Utilisation de g ++ 4.3.2 ici sur Debian, les paramètres n'ont pas d'importance, il suffit d'appeler "g ++ test.cpp", ajouter -Wall, -pedantic ou les deux ne change rien. Il semble que cet avertissement ait été ajouté quelque part après 4.1/4.2. – schnaader

6

Lorsque vous écrivez ceci:

char *b = "bar"; 

le compilateur alloue une zone mémoire anonyme (sans nom) pour stocker la chaîne « bar ». Les littéraux de chaîne ne peuvent pas être modifiés, de sorte que le compilateur (avec l'aide de l'éditeur de liens et du système d'exploitation) place la chaîne littérale dans une partie de l'espace mémoire du programme en cours d'écriture. Lorsque vous essayez de le modifier, le système d'exploitation l'attrape et provoque une erreur de segmentation de votre programme.

(Votre code est C++, C pas, mais ce n'est pas pertinent à cette question.)

-1

Cette différence est peut-être compilateur spécifique. Pour démontrer votre point, utilisez malloc pour allouer le tampon, puis copiez la chaîne dans ce tampon et n'oubliez pas d'utiliser free lorsque vous n'avez plus besoin de la chaîne.

2

Lorsque vous écrivez:

char *foo = "bar"; 

Que se passe réellement est que la « barre » est stockée dans le segment de la mémoire en lecture seule. Par conséquent, il est immuable. Vous obtenez un segfault parce que vous essayez de modifier un segment en lecture seule.

2

Vous pouvez également montrer que 'a' a été modifié en imprimant la valeur du pointeur.

#include <iostream> 
using namespace std; 
int main() 
{ 
    char* a = "foo"; 
    char* b = "bar"; 
    a = b; 

    cout << (void*)a << ", " << (void*)b << endl; 
} 

Cela affichera l'adresse vers laquelle 'a' et 'b' pointent.
Vous devez convertir en 'void *' car l'opérateur < < est surchargé pour 'char *' afin d'imprimer la chaîne. Tout autre pointeur imprimera l'adresse.

1

En théorie, un littéral de chaîne ne devrait pas pouvoir être assigné à un char *, seulement un 'const char *'. Ensuite, le compilateur vous arrêtera avant d'avoir écrit le code de faute de seg.

Questions connexes