Langage C - C/C++ Erreur de segmentation

[Dal] Messages postés 6174 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 2 février 2024 - 30 mai 2022 à 01:42
Note : kilian est l'auteur d'origine de l'astuce.


Qu'est ce qu'une erreur de segmentation

Vous êtes en train de développer une application sous Linux en
C/C++. Tout va bien, ça compile, les oiseaux chantent.
Donc vous lancez votre application pour la tester.

Et vous obtenez l'un de ces deux messages:
Erreur de segmentation

ou
Segmentation fault

Autant lorsque vous avez une erreur avec un programme écrit dans un langage de très haut niveau (perl, python, java etc...), vous aurez un message de debuggage détaillé: quel type d'exception s'est produit? A quel ligne du programme ça correspond etc...
Autant avec un langage compilé (donc transformé en langage directement compréhensible par votre processeur) comme c ou c++, votre programme n'est sous la tutelle d'aucun interpréteur ou machine virtuelle. Donc personne pour surveiller vos erreurs et les analyser.

Heureusement il existe des programmes appelés debuggeurs et qui sont là pour vous faciliter la tâche.

Il faut savoir qu'un système d'exploitation moderne alloue une portion de mémoire pour chaque application. Si une application essaie de pénétrer directement dans une zone mémoire qui ne lui appartient pas ou dans une zone mémoire incorrecte, le système d'exploitation va stopper l'application en cours et va générer une erreur (sous Linux: Erreur de segmentation).

En C

Prenons par exemple un programme en C tout à fait débile qui se plante volontairement et génère une erreur de segmentation :

void lance_erreur_segmentation()
{
 int *pointeur_dangereux=(int *) 100;
 int test=*pointeur_dangereux;
}

int main(int argc, char ** argv)
{
 lance_erreur_segmentation();
 return 0;
}


Dans la fonction lance_erreur_segmentation, le pointeur_dangereux pointe vers l'adresse 100 dans la mémoire.

C'est une adresse qui ne peut pas appartenir à une application normale.

Au moment où pointeur_dangereux tentera de lire ce qu'il ya dans l'adresse mémoire 100 pour mettre la valeur située à cette adresse dans la variable test, le programme va crasher.
Et vous obtiendrez le message "Erreur de segmentation".

Pour débugger ce programme nous allons commencer par le compiler en lui chargeant ses symboles de débuggage. Cela permet notamment de charger les noms de fonctions et variables utilisés dans le programme une fois qu'il est compilé, ainsi le debugger pourra tracer l'erreur en vous indiquant les noms des fonctions concernées plutôt que de donner juste leur adresse.

Je parle ici de l'option -g

On compile:
gcc -g test.c -o test

Et on lance le programme dans le debugger:
gdb ./test

Vous allez apercevoir une invite de commande commençant par:
(gdb)

Tapez la commande run

Observons ce qu'il se passe:
(gdb) run
Starting program: ~/test

Program received signal SIGSEGV, Segmentation fault.
0x08048334 in lance_erreur_segmentation () at test.c:4
4 int test=*pointeur_dangereux;


Que demander de plus: le debuggeur vous indique la ligne dans votre fichier source qui est à l'origine de l'erreur, ainsi que la fonction concernée et le contenu de la ligne:
C'est une erreur dans la fonction lance_erreur_segmentation dans le fichier source test.c à la ligne 4 dont le contenu est
int test=*pointeur_dangereux;


Admettons maintenant que vous vouliez savoir quelle fonction a appelé quelle autre fonction depuis le début du programme jusqu'à l'arrivée de l'erreur.

Tapez la commande bt:
(gdb) bt
#0 0x08048334 in lance_erreur_segmentation () at test.c:4
#1 0x0804834e in main () at test.c:9

Et vous avez encore une fois ce qu'il vous faut :-)

En C++

Pour en rester au même problème: les erreurs de segmentation, il n'y a pas grand chose à ajouter pour le C++, hormis qu'il s'agit d'utiliser la commande g++ avec l'option -g de la même façon qu'avec gcc.

Le rapport de gdb sera le même mais avec des détails sur les classes concernées.

Exemple:
//Fichier test.cpp
class Test{
 public:
  int a;
  Test(){};
  ~Test(){};
  int incremente_a(){ a++; };
};

int main()
{
 Test *t;
 t=(Test *)100;
 t->incremente_a();
 return 0;
}

On compile comme ceci:
g++ -g test.cpp -o test


On lance le déboggeur:
gdb ./test

Puis on y tape les mêmes commandes qu'avant et tous les détails s'affichent:
(gdb) run
Starting program: ~/test

Program received signal SIGSEGV, Segmentation fault.
0x080483fc in Test::incremente_a (this=0x64) at test.cpp:7
7 int incremente_a(){ a++; };
(gdb) bt
#0 0x080483fc in Test::incremente_a (this=0x64) at test.cpp:7
#1 0x080483e7 in main () at test.cpp:14


Et voilà.

Liens