Pourquoi ce code C d'inversion de chaîne provoque-t-il un défaut de segmentation? [dupliquer]

cette question a déjà une réponse ici:

j'essaie d'écrire du code pour inverser une chaîne en place (j'essaie juste de m'améliorer à la programmation C et la manipulation de pointeur), mais je ne peux pas comprendre pourquoi je reçois un segmentation fault :

#include <string.h>

void reverse(char *s);

int main() {
    char* s = "teststring";
    reverse(s);

    return 0;
}

void reverse(char *s) {
    int i, j;
    char temp;

    for (i=0,j = (strlen(s)-1); i < j; i++, j--) {
        temp = *(s+i);     //line 1
        *(s+i) = *(s+j);   //line 2
        *(s+j) = temp;     //line 3
    }
}

ce sont les lignes 2 et 3 qui causent la faille de segmentation. Je comprends qu'il peut y avoir de meilleures façons de le faire, mais je suis intéressé à découvrir ce qui spécifiquement dans mon code est à l'origine de la faille de segmentation.

mise à Jour : j'ai inclus l'appelant fonction demandée.

29
demandé sur Antti Haapala 2009-10-23 21:01:29

8 réponses

il n'y a aucun moyen de dire à partir de ce code. Très probablement, vous passez dans un pointeur qui pointe vers la mémoire invalide, la mémoire non modifiable ou une autre sorte de mémoire qui ne peut tout simplement pas être traitée de la façon dont vous le traitez ici.

Comment appelez-vous votre fonction?

, a Ajouté le: Vous êtes de passage dans un pointeur vers une chaîne littérale. Les caractères littéraux des cordes ne sont pas modifiables. On ne peut pas inverser une chaîne de caractères.

Passer dans un pointer vers une chaîne modifiable à la place

char s[] = "teststring";
reverse(s); 

cela a déjà été expliqué à la mort ici. "teststring" est une chaîne littérale. La chaîne littérale elle-même est un objet non modifiable. En pratique, les compilateurs peuvent (et vont) le mettre dans une mémoire en lecture seule. Quand vous initialisez un pointeur comme ça

char *s = "teststring";

le pointeur pointe directement au début de la chaîne littérale. Toute tentative de modifier ce s vers sont considérés comme ayant échoué dans les cas généraux. Vous pouvez le lire, mais vous ne pouvez pas écrire. Pour cette raison, il est fortement recommandé de pointer vers la chaîne littérales avec pointer-to-const variables seulement

const char *s = "teststring";

mais quand vous déclarez votre s comme

char s[] = "teststring";

vous obtenez un tableau complètement indépendant s situé dans la mémoire modifiable ordinaire, qui est juste initialisé avec la chaîne littérale. Cela signifie que ce tableau modifiable indépendant s obtiendra sa valeur initiale copié à partir de la chaîne literal. Après cela, votre tableau s et la chaîne littérale continuent d'exister en tant qu'objets complètement indépendants. Le littéral est toujours non modifiable, tandis que votre tableau s est modifiable.

fondamentalement, cette dernière déclaration est fonctionnellement équivalente à

char s[11];
strcpy(s, "teststring");
50
répondu AnT 2009-10-24 15:22:29

Vous code pourrait être segfaulting pour un certain nombre de raisons. Voici ceux qui viennent à l'esprit

  1. est NULL
  2. s indique une chaîne de const qui est conservée en mémoire en lecture seule
  3. s n'est pas NULL

je pense que #2 est le plus probable. Pouvez-vous nous montrer le site d'appel de l'inverse?

MODIFIER

Basé sur votre échantillon #2 est certainement la réponse. Une chaîne littérale en C / C++ n'est pas modifiable. Le type approprié est en fait const char* et non char* . Ce que vous devez faire est de passer une chaîne modifiable dans ce tampon.

Rapide exemple:

char* pStr = strdup("foobar");
reverse(pStr);
free(pStr);
9
répondu JaredPar 2009-10-23 17:09:45

est-ce que tu testes quelque chose comme ça?

int main() {
    char * str = "foobar";
    reverse(str);
    printf("%s\n", str);
}

cela rend str une chaîne littérale et vous ne pourrez probablement pas l'éditer (segfaults pour moi). Si vous définissez char * str = strdup(foobar) cela devrait fonctionner très bien (fait pour moi).

2
répondu Cascabel 2009-10-23 17:08:50

votre déclaration est complètement fausse:

char* s = "teststring";

"test" est stocké dans le segment de code, qui est en lecture seule, comme le code. Et, s est un pointeur vers "teststring", en même temps, vous essayez de changer la valeur d'une plage de mémoire en lecture seule. Ainsi, faute de segmentation.

mais avec:

char s[] = "teststring";

s est initialisé avec "teststring", qui se trouve bien sûr dans le segment de code, mais il y a un autre l'opération de copie en cours, à la pile dans ce cas.

2
répondu yogman 2009-10-23 18:37:49

le compilateur et débogueur utilisez-vous? En utilisant gcc et gdb, Je compilerais le code avec l'option-g puis je l'exécuterais dans gdb. Quand il segfaults, je voudrais juste faire une rétrotrace (commande bt dans gdb) et voir quelle est la ligne fautive causant le problème. En outre, je voudrais juste exécuter le code étape par étape, tout en "regardant" les valeurs du pointeur dans gdb et de savoir exactement où est le problème.

bonne chance.

0
répondu user193272 2009-10-23 17:09:56

comme certaines des réponses fournies ci-dessus, la mémoire de chaîne de caractères est en lecture seule. Cependant, certains compilateurs offrent une option de compilation avec des chaînes de caractères en écriture. Par exemple: avec gcc , 3.les versions x ont pris en charge -fwritable-strings mais pas les versions plus récentes.

0
répondu Vishal 2017-07-11 09:56:13

voir Question 1.32 dans la liste c FAQ:

Quelle est la différence entre ces initialisations?

char a[] = "string literal";
char *p  = "string literal";

mon programme s'écrase si j'essaie d'attribuer une nouvelle valeur à p[i] .

réponse:

une chaîne littérale (le terme formel pour une chaîne double citation en C source) peut être utilisée de deux façons légèrement différentes:

Comme l'initialiseur, pour un tableau de char, comme dans la déclaration de char a[] , il spécifie les valeurs initiales des personnages dans ce tableau (et, si nécessaire, sa taille).

ailleurs, il se transforme en un tableau statique sans nom de caractères, et ce tableau sans nom peut être stocké dans une mémoire en lecture seule, et qui ne peut donc pas nécessairement être modifié . Dans un contexte d'expression, le tableau est converti immédiatement en pointeur, comme d'habitude (voir section 6), la deuxième déclaration initialise p pour pointer vers le premier élément du tableau sans nom.

certains compilateurs ont un commutateur contrôlant si les caractères alphabétiques des chaînes sont inscriptibles ou non (pour compiler le vieux code), et certains peuvent avoir des options pour faire en sorte que les caractères alphabétiques des chaînes soient formellement traités comme des tableaux de const char (pour une meilleure capture d'erreur).

( c'est moi qui souligne )

Voir aussi Back to Basics par Joel .

0
répondu Sinan Ünür 2018-01-17 12:38:23

je pense strlen ne peut pas fonctionner puisque s n'est pas une valeur NULL. Si le comportement de votre pour l'itération n'est pas celui que vous attendez. Puisque le résultat de strlen sera supérieur à la longueur de s vous écrirez dans la mémoire où vous ne devriez pas être.

indique en plus une chaîne constante maintenue par une mémoire en lecture seule. Vous ne pouvez pas le modifier. Essayez d'entrer en utilisant la fonction gets comme il est fait dans le strlen exemple

-1
répondu Patrice Bernassola 2009-10-23 17:17:44