Che cos'è un errore di segmentazione?


599

Che cos'è un errore di segmentazione? È diverso in C e C ++? In che modo sono correlati guasti di segmentazione e puntatori pendenti?


95
un errore di segmentazione fa stare male il compilatore .
Benjamin Crouzier,

22
In tal caso, perché nel mio caso il compilatore non si è lamentato di nulla, tutto è andato liscio, ma in fase di esecuzione il sistema genera un errore di segmentazione (core dump)? T_T
Jim Raynor,

3
Solo una discarica di memoria quando qualcosa va storto!
risultati

7
@pinouchon: Divertente, ma quando un compilatore ha a che fare con i guasti di seg? Non è più l'ambiente di runtime?
Dhein,

1
In genere viene chiamato tentando di dereferenziare un puntatore nullo, quindi un errore di segmentazione è spesso analogo a un Java NullPointerException.
Raedwald,

Risposte:


674

L'errore di segmentazione è un tipo specifico di errore causato dall'accesso alla memoria che "non ti appartiene". È un meccanismo di supporto che ti impedisce di corrompere la memoria e di introdurre bug di memoria difficili da eseguire il debug. Ogni volta che ottieni un segfault sai che stai facendo qualcosa di sbagliato con la memoria - accedere alla variabile che è già stata liberata, scrivere su una parte di sola lettura della memoria, ecc. L'errore di segmentazione è essenzialmente lo stesso nella maggior parte delle lingue che ti permettono di fare confusione la gestione della memoria, non vi è alcuna differenza principale tra i segfault in C e C ++.

Esistono molti modi per ottenere un segfault, almeno nei linguaggi di livello inferiore come C (++). Un modo comune per ottenere un segfault è di dereferenziare un puntatore nullo:

int *p = NULL;
*p = 1;

Un altro segfault si verifica quando si tenta di scrivere su una parte della memoria contrassegnata come di sola lettura:

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

Puntatore ciondolante punta a una cosa che non esiste più, come qui:

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

Il puntatore ppende perché indica la variabile di carattere cche ha cessato di esistere dopo la fine del blocco. E quando provi a dereferenziare un puntatore penzolante (come *p='A'), probabilmente otterrai un segfault.


154
L'ultimo esempio è particolarmente brutto, quando costruisco: int main () {char * p = 0; {char c = 'x'; p = & c; } printf ("% c \ n", * p); ritorna 0; } Con gcc o molti altri compilatori, "sembra" funzionare. Nessun avviso durante la compilazione. No segfault. Questo perché "}" non rientra nell'ambito di applicazione, in realtà non elimina i dati, ma li contrassegna come liberi per essere riutilizzati. Il codice può funzionare bene su un sistema di produzione per anni, puoi modificare un'altra parte del codice, cambiare compilatore o qualcos'altro e BOOOOOM!
Chris Huang-Leaver,

36
Ci scusiamo per il bernoccolo ma solo una nota a
margine

18
@oldrinb: è impossibile scrivere codice che causa necessariamente un segfault. Non da ultimo perché ci sono sistemi là fuori che funzionano senza protezione della memoria, quindi non possono dire se un pezzo di memoria effettivamente "appartiene a te", e quindi non conoscono segfault, solo un comportamento indefinito ... (ad esempio il classico AmigaOS)
DevSolar,

7
@ ChrisHuang-Leaver, devi capire che cè locale, significa che è stato inserito nello stack dopo {e pop-ed dopo }. il puntatore pendente è solo un riferimento a un offset che è ora fuori dallo stack. ecco perché modificarlo in un semplice programma non attiverà mai alcun segfault. d'altra parte può portare alla segfault in un caso d'uso più complesso, in cui altre chiamate di funzione potrebbero far crescere lo stack e contenere i dati a cui punta il puntatore penzolante. scrivere su quei dati (variabili locali) porterebbe a comportamenti indefiniti (segfault & Co)
Ayman Khamouma,

3
@ ChrisHuang-Leaver, normalmente quando esci dall'ambito, il compilatore deve recuperare un po 'di spazio dello stack per liberare lo spazio dello stack inutilizzato, ma questo non succede sempre (con gcc come uno di questi compilatori). Inoltre, lo spazio dello stack allocato viene normalmente riutilizzato di nuovo, quindi non ho sentito parlare di sistemi operativi che restituiscono al sistema pagine dello stack inutilizzate, rendendo tale spazio soggetto a a SIGSEGV, quindi non mi aspetto che un tale segnale si rovini con lo stack.
Luis Colorado,

111

Vale la pena notare che l'errore di segmentazione non è causato dall'accesso diretto a un'altra memoria di processo (questo è ciò che sento a volte), poiché semplicemente non è possibile. Con la memoria virtuale ogni processo ha il suo spazio di indirizzi virtuale e non c'è modo di accedervi con qualsiasi valore di puntatore. Un'eccezione a questo può essere librerie condivise che sono lo stesso spazio di indirizzi fisico mappato su (possibilmente) indirizzi virtuali e memoria del kernel diversi che sono persino mappati allo stesso modo in ogni processo (per evitare il flushing TLB su syscall, penso). E cose come shmat;) - questi sono quelli che considero un accesso "indiretto". Tuttavia, è possibile verificare che si trovino di solito molto distanti dal codice di processo e che di solito siamo in grado di accedervi (ecco perché sono lì,

Tuttavia, può verificarsi un errore di segmentazione in caso di accesso alla nostra memoria (di processo) in modo improprio (ad esempio, tentando di scrivere nello spazio non scrivibile). Ma la ragione più comune per questo è l'accesso alla parte dello spazio di indirizzi virtuale che non è affatto mappata a quella fisica.

E tutto questo rispetto ai sistemi di memoria virtuale.


Con la memoria condivisa / i file associati alla memoria è possibile che qualcun altro rovini la tua memoria. In WIN32 ci sono anche cattive API come 'WriteProcessMemory'!
paulm,

1
@paulm: Sì, lo so. Questo è ciò che avevo in mente in "E cose come shmat;) - questi sono quelli che considero un accesso 'indiretto'.
konrad.kruczynski,

In un sistema operativo di memoria virtuale non esiste alcun modo (normalmente, quindi, per favore, gli implementatori del sistema operativo, non licenziarmi per questo) che un processo acceda a un'altra memoria virtuale di processo, non essendo una sorta di chiamata di sistema che collega la memoria che ti consente di accesso. Gli indirizzi di memoria virtuale normalmente significano cose diverse a seconda del processo considerato.
Luis Colorado,

38

Un errore di segmentazione è causato da una richiesta per una pagina che il processo non ha elencato nella sua tabella descrittiva o da una richiesta non valida per una pagina che ha elencato (ad esempio una richiesta di scrittura su una pagina di sola lettura).

Un puntatore pendente è un puntatore che può o meno puntare a una pagina valida, ma punta a un segmento di memoria "imprevisto".


10
Questo è vero, ma ti sarebbe davvero d'aiuto se non sapessi già cos'è un errore di segmentazione?
zoul

29

Ad essere onesti, come hanno già detto altri poster, Wikipedia ha un ottimo articolo su questo, quindi dai un'occhiata qui. Questo tipo di errore è molto comune e spesso chiamato altre cose come violazione di accesso o errore di protezione generale.

Non sono diversi in C, C ++ o in qualsiasi altro linguaggio che consenta i puntatori. Questi tipi di errori sono generalmente causati da puntatori che lo sono

  1. Utilizzato prima di essere inizializzato correttamente
  2. Utilizzato dopo che la memoria a cui puntano è stata riallineata o eliminata.
  3. Utilizzato in una matrice indicizzata in cui l'indice è esterno ai limiti della matrice. Questo è generalmente solo quando si esegue la matematica del puntatore su matrici o stringhe c tradizionali, non su raccolte basate su STL / Boost (in C ++).

16

Secondo Wikipedia:

Si verifica un errore di segmentazione quando un programma tenta di accedere a una posizione di memoria a cui non è consentito accedere o tenta di accedere a una posizione di memoria in un modo non consentito (ad esempio, tentando di scrivere in una posizione di sola lettura, oppure per sovrascrivere parte del sistema operativo).


13

L'errore di segmentazione è anche causato da guasti hardware, in questo caso le memorie RAM. Questa è la causa meno comune, ma se non trovi un errore nel tuo codice, forse un memtest potrebbe aiutarti.

La soluzione in questo caso, cambia la RAM.

modificare:

Qui c'è un riferimento: errore di segmentazione dell'hardware


3
Un test rapido e sporco per RAM difettosa consiste nell'eseguire ripetutamente il programma in crash. Se il programma non ha alcun non determinismo interno, ovvero produce sempre lo stesso output per lo stesso input, o almeno dovrebbe, ma, per alcuni input particolari, si arresta in modo anomalo a volte , non sempre ma neanche mai: allora dovresti iniziare a preoccuparsi della RAM difettosa.
zwol,

8

L'errore di segmentazione si verifica quando un processo (esecuzione dell'istanza di un programma) sta tentando di accedere all'indirizzo di memoria di sola lettura o all'intervallo di memoria utilizzato da un altro processo o accedere all'indirizzo di memoria inesistente (non valido). Il problema Dangling Reference (puntatore) indica che il tentativo di accedere a un oggetto o una variabile il cui contenuto è già stato eliminato dalla memoria, ad esempio:

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

4
Il modo corretto di eliminare un array è delete [] arr;
Damian,

8

La pagina Segmentation_fault di Wikipedia ha una bella descrizione a riguardo, indicando solo le cause e le ragioni. Dai un'occhiata al wiki per una descrizione dettagliata.

Nell'informatica, un errore di segmentazione (spesso abbreviato in segfault) o una violazione di accesso è un errore generato dall'hardware con protezione della memoria, che notifica a un sistema operativo (OS) una violazione dell'accesso alla memoria.

Di seguito sono riportate alcune cause tipiche di un errore di segmentazione:

  • Dereferenziazione dei puntatori NULL: questo è un caso speciale dell'hardware di gestione della memoria
  • Tentativo di accedere a un indirizzo di memoria inesistente (al di fuori dello spazio degli indirizzi del processo)
  • Tentare di accedere alla memoria per cui il programma non ha diritti (come le strutture del kernel nel contesto del processo)
  • Tentativo di scrivere memoria di sola lettura (come un segmento di codice)

Questi a loro volta sono spesso causati da errori di programmazione che provocano un accesso alla memoria non valido:

  • Dereferenziazione o assegnazione a un puntatore non inizializzato (puntatore jolly, che punta a un indirizzo di memoria casuale)

  • Dereferenziazione o assegnazione a un puntatore liberato (puntatore pendente, che punta alla memoria che è stata liberata / deallocata / eliminata)

  • Un buffer overflow.

  • Un overflow dello stack.

  • Tentativo di eseguire un programma che non viene compilato correttamente. (Alcuni compilatori genereranno un file eseguibile nonostante la presenza di errori durante la compilazione.)


6

In parole semplici: errore di segmentazione è il sistema operativo che invia un segnale al programma dicendo che ha rilevato un accesso illegale alla memoria e sta terminando prematuramente il programma per evitare che la memoria venga danneggiata.


3

"Errore di segmentazione" significa che hai tentato di accedere alla memoria a cui non hai accesso.

Il primo problema riguarda i tuoi argomenti di main. La funzione principale dovrebbe essere int main(int argc, char *argv[])e dovresti controllare che argc sia almeno 2 prima di accedere ad argv [1].

Inoltre, poiché stai passando un float a printf (che, a proposito, viene convertito in doppio quando passi a printf), dovresti usare l'identificatore di formato% f. L'identificatore di formato% s è per stringhe (array di caratteri con terminazione '\ 0').


2

Si verifica un errore di segmentazione o una violazione di accesso quando un programma tenta di accedere a una posizione di memoria che non esiste o tenta di accedere a una posizione di memoria in un modo non consentito.

 /* "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;

Qui non [1000] non esisto, quindi si verifica segfault.

Cause dell'errore di segmentazione:

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
Innanzitutto, la seg seg non ha nulla a che fare con l'indirizzo esiste o non esiste. Si tratta di accedervi dove non ti è permesso farlo. E nel tuo esempio speciale è persino garantito dallo standard che quella posizione esiste. poiché lo standard dice in caso di array si deve dare che esiste un indirizzo valido per un puntatore pointg su un array ben allineato all'interno dei suoi limiti E 1 dietro .
Dhein,

viene anche reindirizzato con l'indirizzo, se non si dispone dell'indirizzo e se si tenta di accedere a questo indirizzo, è presente anche seg. colpa. E nel mio esempio, è solo per capire il punto di vista.
Mohit Rohilla,

2

Ci sono molte buone spiegazioni di "Errore di segmentazione" nelle risposte, ma poiché con errore di segmentazione spesso c'è un dump del contenuto della memoria, volevo condividere dove la relazione tra la parte "core dumped" nell'errore di segmentazione (core dumped) e la memoria viene da:

Dal 1955 al 1975 circa - prima della memoria dei semiconduttori - la tecnologia dominante nella memoria del computer utilizzava piccole ciambelle magnetiche infilate su fili di rame. Le ciambelle erano conosciute come "nuclei di ferrite" e memoria principale così conosciuta come "memoria centrale" o "nucleo".

Tratto da qui .


2

Ci sono abbastanza definizioni di errore di segmentazione, vorrei citare alcuni esempi che ho riscontrato durante la programmazione, che potrebbero sembrare errori sciocchi, ma perderanno molto tempo.

  1. è possibile ottenere un errore di segmentazione nel caso seguente mentre il tipo argumet non corrisponde a printf

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

produzione : Segmentation Fault (SIGSEGV)

  1. quando hai dimenticato di allocare memoria a un puntatore, ma provi a usarlo.

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

produzione : Segmentation Fault (SIGSEGV)


1

Il semplice significato di Segmentation faultè che stai provando ad accedere ad una memoria che non ti appartiene. Segmentation faultsi verifica quando tentiamo di leggere e / o scrivere attività in una posizione di memoria di sola lettura o tentiamo di liberare memoria. In altre parole, possiamo spiegarlo come una sorta di corruzione della memoria.

Di seguito menziono errori comuni commessi da programmatori che portano a Segmentation fault.

  • Utilizzare scanf()in modo errato (dimenticato di mettere &).
int num;
scanf("%d", num);// must use &num instead of num
  • Usa i puntatori nel modo sbagliato.
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.
  • Modifica di una stringa letterale (il puntatore tenta di scrivere o modificare una memoria di sola lettura).
char *str;  

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

//Problem:  trying to modify read only memory
*(str+1) = 'n';
  • Cerca di raggiungere attraverso un indirizzo che è già stato liberato.
// 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; 
  • Stack Overflow: esaurisce la memoria nello stack
  • Accesso a un array fuori limite
  • Utilizzare identificatori di formato errati quando si utilizza printf()e scanf()"
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.