Qu'est-ce qu'un défaut de segmentation?


599

Qu'est-ce qu'un défaut de segmentation? Est-ce différent en C et C ++? Comment les défauts de segmentation et les pointeurs pendants sont-ils liés?


95
une erreur de segmentation rend le compilateur mal à l'aise .
Benjamin Crouzier

22
Si tel est le cas, pourquoi dans mon cas le compilateur ne se plaignait de rien, tout s'est bien passé, mais au moment de l'exécution, le système lance une erreur de segmentation (vidage de mémoire)? T_T
Jim Raynor

3
Juste un vidage de mémoire en cas de problème!
resultsway

7
@pinouchon: C'est drôle, mais quand un compilateur a-t-il quelque chose à voir avec les erreurs de segmentation? N'est-ce pas plutôt l'environnement d'exécution?
dhein

1
Généralement appelé en tentant de déréférencer un pointeur nul, une erreur de segmentation est donc souvent analogue à Java NullPointerException.
Raedwald

Réponses:


674

La faute de segmentation est un type spécifique d'erreur causée par l'accès à la mémoire qui «ne vous appartient pas». C'est un mécanisme d'aide qui vous empêche de corrompre la mémoire et d'introduire des bogues de mémoire difficiles à déboguer. Chaque fois que vous obtenez une erreur de segmentation, vous savez que vous faites quelque chose de mal avec la mémoire - accéder à une variable qui a déjà été libérée, écrire dans une partie en lecture seule de la mémoire, etc. L'erreur de segmentation est essentiellement la même dans la plupart des langues qui vous permettent de jouer avec la gestion de la mémoire, il n'y a pas de différence principale entre les segfaults en C et C ++.

Il existe de nombreuses façons d'obtenir une erreur de segmentation, au moins dans les langages de niveau inférieur tels que C (++). Une façon courante d'obtenir un défaut de segmentation est de déréférencer un pointeur nul:

int *p = NULL;
*p = 1;

Une autre erreur de segmentation se produit lorsque vous essayez d'écrire sur une partie de la mémoire qui a été marquée en lecture seule:

char *str = "Foo"; // Compiler marks the constant string as read-only
*str = 'b'; // Which means this is illegal and results in a segfault

Le pointeur pendant fait pointer vers une chose qui n'existe plus, comme ici:

char *p = NULL;
{
    char c;
    p = &c;
}
// Now p is dangling

Le pointeur est psuspendu car il pointe vers une variable de caractère cqui a cessé d'exister après la fin du bloc. Et lorsque vous essayez de déréférencer un pointeur suspendu (comme *p='A'), vous obtiendrez probablement un défaut de segmentation.


154
Le dernier exemple est particulièrement méchant, quand je construis: int main () {char * p = 0; {char c = 'x'; p = & c; } printf ("% c \ n", * p); retourner 0; } Avec gcc ou plusieurs autres compilateurs, cela «semble» fonctionner. Aucun avertissement lors de la compilation. Pas de faute de segmentation. Cela est dû au fait que le '}' est hors de portée, ne supprime pas réellement les données, les marque simplement comme libres d'être réutilisées. Le code peut fonctionner correctement sur un système de production pendant des années, vous modifiez une autre partie du code, changez le compilateur ou autre chose et BOOOOOM!
Chris Huang-Leaver

36
Désolé pour la bosse mais juste une note latérale ... aucun de vos exemples ne provoque nécessairement une erreur de segmentation, en fait c'est juste un comportement indéfini ;-)
oldrinb

18
@oldrinb: Il est impossible d'écrire du code qui provoque nécessairement une erreur de segmentation. Pas moins parce qu'il existe des systèmes qui fonctionnent sans protection de la mémoire, ne peuvent donc pas dire si un morceau de mémoire "vous appartient" et ne connaissent donc pas les défauts de segmentation, seulement un comportement indéfini ... (AmigaOS classique, par exemple)
DevSolar

7
@ ChrisHuang-Leaver, vous devez comprendre que cc'est local, cela signifie qu'il a été poussé sur la pile après {et en est sorti après }. le pointeur pendant n'est qu'une référence à un décalage qui est maintenant hors de la pile. c'est pourquoi le modifier dans un programme simple ne déclenchera jamais de défaut de segmentation. d'un autre côté, cela peut conduire à une erreur de segmentation dans un cas d'utilisation plus complexe, où d'autres appels de fonction peuvent entraîner la croissance de la pile et contenir les données pointées par le pointeur pendant. écrire sur ces données (vars locaux) entraînerait un comportement indéfini (segfault & Co)
Ayman Khamouma

3
@ ChrisHuang-Leaver, normalement lorsque vous sortez de la portée, le compilateur doit récupérer de l'espace de pile pour libérer l'espace de pile inutilisé, mais cela ne se produit pas toujours (avec gcc étant l'un de ces compilateurs). De plus, l'espace de pile alloué est normalement réutilisé à nouveau, donc j'ai entendu parler d'aucun système d'exploitation qui ne renvoie les pages de pile inutilisées au système, faisant de cet espace un sujet SIGSEGV, donc je ne m'attendrai pas à un tel signal en manipulant la pile.
Luis Colorado

111

Il convient de noter que la faute de segmentation n'est pas causée par l'accès direct à une autre mémoire de processus (c'est ce que j'entends parfois), car ce n'est tout simplement pas possible. Avec la mémoire virtuelle, chaque processus a son propre espace d'adressage virtuel et il n'y a aucun moyen d'accéder à un autre en utilisant une valeur de pointeur. Une exception à cela peut être les bibliothèques partagées qui sont le même espace d'adressage physique mappé à (éventuellement) différentes adresses virtuelles et la mémoire du noyau qui est même mappée de la même manière dans chaque processus (pour éviter le vidage TLB sur syscall, je pense). Et des choses comme shmat;) - c'est ce que je considère comme un accès «indirect». On peut cependant vérifier qu’ils sont généralement situés loin du code de processus et que nous pouvons généralement y accéder (c’est pourquoi ils sont là,

Néanmoins, une erreur de segmentation peut se produire en cas d'accès incorrect à notre propre mémoire (de processus) (par exemple, en essayant d'écrire sur un espace non accessible en écriture). Mais la raison la plus courante en est l'accès à la partie de l'espace d'adressage virtuel qui n'est pas du tout mappée à un espace physique.

Et tout cela en ce qui concerne les systèmes de mémoire virtuelle.


Avec la mémoire partagée / les fichiers mappés en mémoire, il est possible pour quelqu'un d'autre de jouer avec votre mémoire. Dans WIN32, il y a aussi des API désagréables comme 'WriteProcessMemory'!
paulm

1
@paulm: Oui, je sais. C'est ce que j'avais en tête dans "Et des choses comme shmat;) - c'est ce que je considère comme un accès" indirect "".
konrad.kruczynski

Dans un système d'exploitation de mémoire virtuelle, il n'y a aucun moyen (normalement, donc s'il vous plaît, les implémenteurs de système d'exploitation, ne me lancez pas pour cela) pour qu'un processus accède à une autre mémoire virtuelle de processus, n'étant pas une sorte d'appel système d'attachement de mémoire qui vous permet de accès. Les adresses de mémoire virtuelle signifient normalement des choses différentes selon le processus considéré.
Luis Colorado

38

Une erreur de segmentation est causée par une demande de page que le processus n'a pas répertoriée dans sa table de descripteurs ou par une demande non valide pour une page qu'il a répertoriée (par exemple, une demande d'écriture sur une page en lecture seule).

Un pointeur pendant est un pointeur qui peut ou non pointer vers une page valide, mais pointe vers un segment de mémoire "inattendu".


10
C'est vrai, mais cela vous aiderait-il vraiment si vous ne saviez pas déjà ce qu'est une erreur de segmentation?
zoul

29

Pour être honnête, comme d'autres affiches l'ont mentionné, Wikipédia a un très bon article à ce sujet, alors lisez-le. Ce type d'erreur est très courant et s'appelle souvent d'autres choses telles que la violation d'accès ou la défaillance de protection générale.

Ils ne sont pas différents en C, C ++ ou tout autre langage qui autorise les pointeurs. Ces types d'erreurs sont généralement causés par des pointeurs qui sont

  1. Utilisé avant d'être correctement initialisé
  2. Utilisé après que la mémoire vers laquelle ils pointent a été réaffectée ou supprimée.
  3. Utilisé dans un tableau indexé où l'index est en dehors des limites du tableau. Ce n'est généralement que lorsque vous effectuez des calculs de pointeur sur des tableaux traditionnels ou des chaînes C, et non sur des collections basées sur STL / Boost (en C ++.)

16

Selon wikipedia:

Une erreur de segmentation se produit lorsqu'un programme tente d'accéder à un emplacement mémoire auquel il n'est pas autorisé à accéder, ou tente d'accéder à un emplacement mémoire d'une manière non autorisée (par exemple, en tentant d'écrire dans un emplacement en lecture seule, ou pour remplacer une partie du système d'exploitation).


13

Le défaut de segmentation est également provoqué par des pannes matérielles, dans ce cas les mémoires RAM. C'est la cause la moins fréquente, mais si vous ne trouvez pas d'erreur dans votre code, peut-être qu'un memtest pourrait vous aider.

La solution dans ce cas, changez la RAM.

Éditer:

Voici une référence: défaut de segmentation par matériel


3
Un test rapide et sale pour la RAM défectueuse consiste à exécuter votre programme en panne encore et encore en boucle. Si le programme n'a pas de non-déterminisme interne - c'est-à-dire qu'il produit toujours la même sortie pour la même entrée, ou du moins qu'il est censé le faire - mais, pour une entrée particulière, il se bloque parfois , pas toujours mais pas non plus: alors vous devriez commencez à vous soucier de la mauvaise RAM.
zwol

8

Une erreur de segmentation se produit lorsqu'un processus (instance en cours d'exécution d'un programme) tente d'accéder à l'adresse mémoire en lecture seule ou à la plage de mémoire utilisée par un autre processus ou d'accéder à l'adresse mémoire inexistante (non valide). Un problème de référence pendante (pointeur) signifie qu'essayer d'accéder à un objet ou une variable dont le contenu a déjà été supprimé de la mémoire, par exemple:

int *arr = new int[20];
delete arr;
cout<<arr[1];  //dangling problem occurs here

4
La façon correcte de supprimer un tableau est de supprimer [] arr;
Damian

8

La page Segmentation_fault de Wikipédia a une très belle description à ce sujet, soulignant simplement les causes et les raisons. Jetez un œil au wiki pour une description détaillée.

En informatique, une erreur de segmentation (souvent raccourcie en erreur de segmentation) ou une violation d'accès est une erreur déclenchée par un matériel avec protection de la mémoire, informant un système d'exploitation (OS) d'une violation d'accès à la mémoire.

Voici quelques causes typiques d'un défaut de segmentation:

  • Déréférencement des pointeurs NULL - c'est un cas particulier du matériel de gestion de la mémoire
  • Tentative d'accès à une adresse mémoire inexistante (en dehors de l'espace d'adressage du processus)
  • Tenter d'accéder à la mémoire sur laquelle le programme n'a pas de droits (comme les structures du noyau dans le contexte du processus)
  • Tentative d'écriture de mémoire morte (comme un segment de code)

Ceux-ci à leur tour sont souvent causés par des erreurs de programmation qui entraînent un accès à la mémoire non valide:

  • Déréférencement ou affectation à un pointeur non initialisé (pointeur sauvage, qui pointe vers une adresse mémoire aléatoire)

  • Déréférencement ou affectation à un pointeur libéré (pointeur suspendu, qui pointe vers la mémoire qui a été libérée / désallouée / supprimée)

  • Un débordement de tampon.

  • Un débordement de pile.

  • Tentative d'exécution d'un programme qui ne se compile pas correctement. (Certains compilateurs afficheront un fichier exécutable malgré la présence d'erreurs de compilation.)


6

En termes simples: une erreur de segmentation est le système d'exploitation envoyant un signal au programme disant qu'il a détecté un accès illégal à la mémoire et qu'il met fin prématurément au programme pour éviter que la mémoire ne soit corrompue.


3

"Erreur de segmentation" signifie que vous avez essayé d'accéder à une mémoire à laquelle vous n'avez pas accès.

Le premier problème concerne vos arguments de main. La fonction principale devrait être int main(int argc, char *argv[]), et vous devriez vérifier que argc est au moins 2 avant d'accéder à argv [1].

De plus, puisque vous passez un flottant à printf (qui, soit dit en passant, est converti en double lors du passage à printf), vous devez utiliser le spécificateur de format% f. Le spécificateur de format% s est pour les chaînes (tableaux de caractères terminés par '\ 0').


2

Une erreur de segmentation ou une violation d'accès se produit lorsqu'un programme tente d'accéder à un emplacement mémoire qui n'existe pas ou tente d'accéder à un emplacement mémoire d'une manière non autorisée.

 /* "Array out of bounds" error 
   valid indices for array foo
   are 0, 1, ... 999 */
   int foo[1000];
   for (int i = 0; i <= 1000 ; i++) 
   foo[i] = i;

Ici, je [1000] n'existe pas, donc une erreur de segmentation se produit.

Causes de défaut de segmentation:

it arise primarily due to errors in use of pointers for virtual memory addressing, particularly illegal access.

De-referencing NULL pointers  this is special-cased by memory management hardware.

Attempting to access a nonexistent memory address (outside processs address space).

Attempting to access memory the program does not have rights to (such as kernel structures in process context).

Attempting to write read-only memory (such as code segment).

2
Tout d'abord, la faute de segmentation n'a rien à voir avec l'adresse qui existe ou qui n'existe pas. Il s'agit de vous y accédez où vous n'êtes pas autorisé à le faire. Et dans votre exemple spécial, il est même garanti par la norme que cet emplacement existe. puisque la norme dit dans le cas d'un tableau, il faut indiquer qu'il y a une adresse valide pour un pointeur pointg sur un tableau bien aligné dans ses limites ET 1 derrière .
dhein

il est également relâché avec l'adresse, si vous n'avez pas l'adresse et si vous essayez d'accéder à cette adresse, il y a aussi seg. faute. Et dans mon exemple, c'est uniquement pour comprendre le point de vue.
Mohit Rohilla

2

Il y a plusieurs bonnes explications de la "faute de segmentation" dans les réponses, mais comme avec la faute de segmentation il y a souvent un vidage du contenu de la mémoire, je voulais partager où la relation entre la partie "vidage de mémoire" dans la faute de segmentation (vidage de mémoire ) et la mémoire vient de:

D'environ 1955 à 1975 - avant la mémoire des semi-conducteurs - la technologie dominante dans la mémoire des ordinateurs utilisait de minuscules beignets magnétiques enfilés sur des fils de cuivre. Les beignets étaient connus sous le nom de "noyaux de ferrite" et la mémoire principale ainsi connue sous le nom de "mémoire centrale" ou "noyau".

Pris d' ici .


2

Il y a suffisamment de définitions de défaut de segmentation, je voudrais citer quelques exemples que j'ai rencontrés lors de la programmation, qui peuvent sembler des erreurs stupides, mais qui perdront beaucoup de temps.

  1. vous pouvez obtenir un défaut de segmentation dans le cas ci-dessous pendant une incompatibilité de type argumet dans printf

    #include<stdio.h> int main(){
    int a = 5; printf("%s",a); return 0; }

production : Segmentation Fault (SIGSEGV)

  1. lorsque vous avez oublié d'allouer de la mémoire à un pointeur, mais que vous essayez de l'utiliser.

     #include<stdio.h> 
     typedef struct{
       int a;
     }myStruct;   
    int main(){
      myStruct *s;
      /* few lines of code */
      s->a = 5;
      return 0;
    }

production : Segmentation Fault (SIGSEGV)


1

Le sens simple de Segmentation faultest que vous essayez d'accéder à une mémoire qui ne vous appartient pas. Segmentation faultse produit lorsque nous tentons de lire et / ou d'écrire des tâches dans un emplacement mémoire en lecture seule ou d'essayer de libérer de la mémoire. En d'autres termes, nous pouvons expliquer cela comme une sorte de corruption de mémoire.

Ci-dessous, je mentionne les erreurs courantes commises par les programmeurs qui conduisent à Segmentation fault.

  • Utiliser scanf()dans le mauvais sens (oublié de mettre &).
int num;
scanf("%d", num);// must use &num instead of num
  • Utilisez des pointeurs dans le mauvais sens.
int *num; 
printf("%d",*num); //*num should be correct as num only
//Unless You can use *num but you have to point this pointer to valid memory address before accessing it.
  • Modification d'un littéral de chaîne (le pointeur essaie d'écrire ou de modifier une mémoire en lecture seule.)
char *str;  

//Stored in read only part of data segment
str = "GfG";      

//Problem:  trying to modify read only memory
*(str+1) = 'n';
  • Essayez de joindre une adresse déjà libérée.
// allocating memory to num 
int* num = malloc(8); 
*num = 100; 

// de-allocated the space allocated to num 
free(num); 

// num is already freed there for it cause segmentation fault
*num = 110; 
  • Débordement de pile -: manque de mémoire sur la pile
  • Accéder à un tableau hors limites '
  • Utilisez des spécificateurs de format incorrects lorsque vous utilisez printf()et scanf()'
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.