Co to jest błąd segmentacji?


598

Co to jest błąd segmentacji? Czy jest inaczej w C i C ++? W jaki sposób powiązane są błędy segmentacji i zwisające wskaźniki?


93
błąd segmentacji powoduje, że kompilator źle się czuje .
Benjamin Crouzier

22
Jeśli tak jest, to dlaczego w moim przypadku kompilator nic nie narzekał, wszystko poszło gładko, ale w czasie wykonywania system zgłasza błąd segmentacji (zrzut rdzenia)? T_T
Jim Raynor

3
Tylko zrzut pamięci, gdy coś pójdzie nie tak!
resultsway

7
@pinouchon: Zabawne, ale kiedy kompilator ma coś wspólnego z błędami seg? Czy nie jest to bardziej środowisko wykonawcze?
dhein

1
Zwykle wywoływany przez próbę wyłożenia wskaźnika zerowego, więc błąd segmentacji jest często analogiczny do Java NullPointerException.
Raedwald,

Odpowiedzi:


673

Błąd segmentacji to szczególny rodzaj błędu spowodowanego dostępem do pamięci, która „nie należy do ciebie”. Jest to mechanizm pomocniczy, który chroni przed uszkodzeniem pamięci i wprowadzaniem trudnych do debugowania błędów pamięci. Za każdym razem, gdy pojawia się awaria, wiesz, że robisz coś złego z pamięcią - uzyskujesz dostęp do zmiennej, która została już zwolniona, zapisujesz w części pamięci tylko do odczytu itp. Błąd segmentacji jest zasadniczo taki sam w większości języków, które pozwalają na bałagan z zarządzanie pamięcią, nie ma zasadniczej różnicy między segfault w C i C ++.

Istnieje wiele sposobów, aby uzyskać awarię, przynajmniej w językach niższego poziomu, takich jak C (++). Typowym sposobem na uzyskanie segfault jest wyzerowanie wskaźnika zerowego:

int *p = NULL;
*p = 1;

Kolejna awaria zdarza się, gdy próbujesz zapisać w części pamięci oznaczonej jako tylko do odczytu:

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

Zwisający wskaźnik wskazuje na rzecz, która już nie istnieje, jak tutaj:

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

Wskaźnik pzwisa, ponieważ wskazuje na zmienną cznakową, która przestała istnieć po zakończeniu bloku. A kiedy spróbujesz wyrejestrować zwisający wskaźnik (jak *p='A'), prawdopodobnie dostaniesz awarię.


154
Ostatni przykład jest szczególnie paskudny, gdy buduję: int main () {char * p = 0; {char c = „x”; p = & c; } printf ("% c \ n", * p); zwraca 0; } Z gcc lub kilkoma innymi kompilatorami „wydaje się” działać. Brak ostrzeżeń podczas kompilacji. Brak segfault. Jest tak, ponieważ „}” poza zakresem, nie usuwa danych, po prostu oznacza, że ​​można je ponownie użyć. Kod może działać dobrze w systemie produkcyjnym przez lata, zmieniasz inną część kodu, zmieniasz kompilator lub coś innego i BOOOOOM!
Chris Huang-Leaver,

36
Przepraszamy za guz, ale tylko na marginesie ... żaden z twoich przykładów niekoniecznie powoduje awarię, w rzeczywistości jest to po prostu niezdefiniowane zachowanie ;-)
oldrinb

18
@oldrinb: Nie można napisać kodu, który koniecznie powoduje awarię. Nie tylko dlatego, że istnieją systemy działające bez ochrony pamięci, dlatego nie można stwierdzić, czy pamięć rzeczywiście „należy do ciebie”, a tym samym nie znają błędów segregacyjnych, tylko niezdefiniowane zachowanie ... (na przykład klasyczny AmigaOS)
DevSolar

7
@ ChrisHuang-Leaver, musisz zrozumieć, że cjest lokalny, oznacza to, że został on wypchnięty na stos później {i wyskakuje z niego później }. zwisający wskaźnik jest tylko odniesieniem do przesunięcia, które jest teraz poza stosem. dlatego modyfikowanie go w prostym programie nigdy nie wywoła żadnej awarii. z drugiej strony może to prowadzić do segfaultu w bardziej złożonym przypadku użycia, gdzie inne wywołania funkcji mogą prowadzić do wzrostu stosu i przechowywania danych wskazanych przez wiszący wskaźnik. zapisywanie do tych danych (lokalne zmienne) prowadziłoby do nieokreślonego zachowania (segfault & Co)
Ayman Khamouma

3
@ ChrisHuang-Leaver, zwykle kiedy wychodzisz poza zasięg, kompilator musi odzyskać trochę miejsca na stosie, aby zwolnić nieużywane miejsce na stosie, ale nie zawsze tak się dzieje (z jednym z tych kompilatorów jest gcc). Ponadto przydzielone miejsce na stosie jest normalnie ponownie wykorzystywane, więc nie słyszałem o systemach operacyjnych, które zwracałyby nieużywane strony stosu do systemu, przez co to miejsce jest przedmiotem SIGSEGV, więc nie oczekuję, że taki sygnał zakłócałby się ze stosem.
Luis Colorado,

111

Warto zauważyć, że błąd segmentacji nie jest spowodowany bezpośrednim dostępem do innej pamięci procesu (to właśnie czasami słyszę), ponieważ jest to po prostu niemożliwe. Dzięki pamięci wirtualnej każdy proces ma własną wirtualną przestrzeń adresową i nie ma możliwości uzyskania dostępu do innej za pomocą dowolnej wartości wskaźnika. Wyjątkiem mogą być biblioteki współdzielone, które są tą samą fizyczną przestrzenią adresową odwzorowaną na (ewentualnie) różne adresy wirtualne i pamięć jądra, która jest nawet odwzorowana w ten sam sposób w każdym procesie (myślę, aby uniknąć opróżniania TLB podczas syscall). I takie rzeczy jak shmat;) - to, co uważam za dostęp „pośredni”. Można jednak sprawdzić, czy zwykle znajdują się one z dala od kodu procesu i zazwyczaj jesteśmy w stanie uzyskać do nich dostęp (dlatego tam są,

Mimo to błąd segmentacji może wystąpić w przypadku niewłaściwego dostępu do naszej (procesowej) pamięci (na przykład próby zapisu w przestrzeni, której nie można zapisać). Ale najczęstszym powodem jest dostęp do części wirtualnej przestrzeni adresowej, która w ogóle nie jest odwzorowana na fizyczną.

A wszystko to w odniesieniu do systemów pamięci wirtualnej.


Dzięki pamięci współdzielonej / plikom mapowanym na pamięć ktoś może zepsuć twoją pamięć. W WIN32 są też nieprzyjemne API takie jak „WriteProcessMemory”!
paulm

1
@paulm: Tak, wiem. Właśnie to miałem na myśli w „I rzeczy takich jak shmat;) - to właśnie uważam za dostęp„ pośredni ”.
konrad.kruczynski

W systemie operacyjnym pamięci wirtualnej nie ma możliwości (normalnie, więc implementatorzy systemu operacyjnego, nie rozpalaj mnie za to), aby proces uzyskał dostęp do pamięci wirtualnej innego procesu, nie będąc połączeniem pamięci systemowej, która umożliwia dostęp. Adresy pamięci wirtualnej zwykle oznaczają różne rzeczy w zależności od rozważanego procesu.
Luis Colorado,

38

Błąd segmentacji jest spowodowany żądaniem strony, której proces nie ma na liście w tabeli deskryptorów, lub nieprawidłowym żądaniem strony, którą ma na liście (np. Żądanie zapisu na stronie tylko do odczytu).

Zwisający wskaźnik to wskaźnik, który może, ale nie musi wskazywać prawidłowej strony, ale wskazuje „nieoczekiwany” segment pamięci.


10
To prawda, ale czy naprawdę by ci to pomogło, gdybyś nie wiedział, co to jest błąd segmentacji?
zoul

29

Szczerze mówiąc, jak wspomnieli inni plakaty, Wikipedia ma na ten temat bardzo dobry artykuł, więc zajrzyj tam. Ten typ błędu jest bardzo powszechny i ​​często nazywany innymi rzeczami, takimi jak naruszenie dostępu lub ogólny błąd ochrony.

Nie różnią się niczym w C, C ++ ani żadnym innym języku, który pozwala na korzystanie ze wskaźników. Tego rodzaju błędy są zwykle powodowane przez wskaźniki

  1. Używany przed właściwą inicjalizacją
  2. Używane po tym, jak pamięć, na którą wskazują, została ponownie przydzielona lub usunięta.
  3. Używany w tablicy indeksowanej, gdzie indeks znajduje się poza granicami tablicy. Zwykle dzieje się tak tylko wtedy, gdy wykonujesz matematykę wskaźników na tradycyjnych tablicach lub ciągach c, a nie na kolekcjach opartych na STL / Boost (w C ++).

16

Według wikipedii:

Błąd segmentacji występuje, gdy program próbuje uzyskać dostęp do lokalizacji pamięci, do której nie ma dostępu, lub próbuje uzyskać dostęp do lokalizacji pamięci w sposób, który jest niedozwolony (na przykład próba zapisu do lokalizacji tylko do odczytu lub nadpisać część systemu operacyjnego).


13

Błąd segmentacji jest również spowodowany awarią sprzętu, w tym przypadku pamięci RAM. Jest to mniej powszechna przyczyna, ale jeśli nie znajdziesz błędu w kodzie, być może pomoże ci memtest.

Rozwiązaniem w tym przypadku jest zmiana pamięci RAM.

edytować:

Oto odniesienie: Błąd segmentacji według sprzętu


3
Szybkim i brudnym testem na wadliwą pamięć RAM jest ciągłe uruchamianie programu powodującego awarię w pętli. Jeśli program nie ma wewnętrznego niedeterminizmu - to znaczy, zawsze wytwarza to samo wyjście dla tego samego wejścia, a przynajmniej powinien - ale w przypadku niektórych danych wejściowych czasami ulega awarii , nie zawsze, ale nie zawsze: wtedy powinieneś zacznij się martwić o złą pamięć RAM.
zwolnienie

8

Błąd segmentacji występuje, gdy proces (działająca instancja programu) próbuje uzyskać dostęp do adresu pamięci tylko do odczytu lub zakresu pamięci, który jest używany przez inny proces lub uzyskać dostęp do nieistniejącego (niepoprawnego) adresu pamięci. Problem Dangling Reference (wskaźnik) oznacza, że ​​próba uzyskania dostępu do obiektu lub zmiennej, której zawartość została już usunięta z pamięci, np .:

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

4
Prawidłowym sposobem usunięcia tablicy jest delete [] arr;
Damian

8

Strona błędu segmentacji Wikipedii ma bardzo ładny opis na ten temat, podając tylko przyczyny i przyczyny. Szczegółowy opis znajduje się na wiki.

W obliczeniach błąd segmentacji (często skracany do segfault) lub naruszenie dostępu to błąd zgłaszany przez sprzęt z ochroną pamięci, powiadamiający system operacyjny (OS) o naruszeniu dostępu do pamięci.

Oto niektóre typowe przyczyny błędu segmentacji:

  • Dereferencing NULL wskaźniki - jest to specjalnie zaprojektowane przez sprzęt do zarządzania pamięcią
  • Próba uzyskania dostępu do nieistniejącego adresu pamięci (poza przestrzenią adresową procesu)
  • Próba dostępu do pamięci program nie ma uprawnień (takich jak struktury jądra w kontekście procesu)
  • Próba zapisu pamięci tylko do odczytu (na przykład segmentu kodu)

Te z kolei są często spowodowane błędami programowania, które powodują nieprawidłowy dostęp do pamięci:

  • Dereferencje lub przypisywanie do niezainicjowanego wskaźnika (dziki wskaźnik, który wskazuje na losowy adres pamięci)

  • Dereferencje lub przypisywanie do zwolnionego wskaźnika (zwisający wskaźnik, który wskazuje na pamięć, która została zwolniona / cofnięta / usunięta)

  • Przepełnienie bufora.

  • Przepełnienie stosu.

  • Próba uruchomienia programu, który nie kompiluje się poprawnie. (Niektóre kompilatory generują plik wykonywalny pomimo obecności błędów czasu kompilacji).


6

Krótko mówiąc: błąd segmentacji to system operacyjny wysyłający do programu sygnał informujący, że wykrył nielegalny dostęp do pamięci i przedwcześnie kończy działanie programu, aby zapobiec uszkodzeniu pamięci.


3

„Błąd segmentacji” oznacza, że ​​próbowałeś uzyskać dostęp do pamięci, do której nie masz dostępu.

Pierwszy problem dotyczy twoich argumentów main. Główną funkcją powinno być int main(int argc, char *argv[])i powinieneś sprawdzić, czy argc wynosi co najmniej 2, zanim uzyskasz dostęp do argv [1].

Ponadto, ponieważ przekazujesz liczbę zmiennoprzecinkową do printf (która, nawiasem mówiąc, przekształca się w podwójną podczas przekazywania do printf), powinieneś użyć specyfikatora formatu% f. Specyfikator formatu% s dotyczy ciągów znaków (tablice znaków zakończone „\ 0”).


2

Błąd segmentacji lub naruszenie dostępu występuje, gdy program próbuje uzyskać dostęp do miejsca pamięci, które nie istnieje lub próbuje uzyskać dostęp do miejsca pamięci w sposób niedozwolony.

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

Tutaj ja [1000] nie istnieje, więc występuje błąd.

Przyczyny błędu segmentacji:

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
Przede wszystkim seg wina nie ma nic wspólnego z tym, że adres istnieje lub nie istnieje. Chodzi o to, że uzyskujesz do niego dostęp tam, gdzie nie możesz tego robić. A w twoim szczególnym przykładzie jest nawet gwarantowane, że ta lokalizacja istnieje. skoro standard mówi w przypadku tablicy, należy podać, że istnieje prawidłowy adres wskaźnika pointg na dobrze wyrównanej tablicy w jej granicach ORAZ 1 z tyłu .
dhein

jest również powiązany z adresem, jeśli nie masz adresu i jeśli próbujesz uzyskać dostęp do tego adresu, istnieje również seg. wina. I w moim przykładzie służy to wyłącznie zrozumieniu punktu widzenia.
Mohit Rohilla,

2

Istnieje kilka dobrych wyjaśnień „błędu segmentacji” w odpowiedziach, ale ponieważ w przypadku błędu segmentacji często występuje zrzut zawartości pamięci, chciałem się podzielić, gdzie jest relacja między częścią „zrzuconego rdzenia” w usterce segmentacji (zrzucony rdzeń) i pamięć pochodzi z:

Od około 1955 do 1975 r. - przed pamięcią półprzewodnikową - dominującą technologią w pamięci komputera były małe magnetyczne pączki naciągnięte na druty miedziane. Pączki były znane jako „rdzenie ferrytowe”, a pamięć główna zwana „pamięcią rdzeniową” lub „rdzeniem”.

Zabrano stąd .


2

Jest wystarczająco dużo definicji błędu segmentacji, chciałbym zacytować kilka przykładów, które napotkałem podczas programowania, które mogą wydawać się głupimi błędami, ale zmarnują dużo czasu.

  1. możesz otrzymać błąd segmentacji w poniższym przypadku, podczas gdy niedopasowanie typu argumet w printf

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

wynik : Segmentation Fault (SIGSEGV)

  1. gdy zapomniałeś przydzielić pamięć wskaźnikowi, ale próbujesz go użyć.

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

wynik : Segmentation Fault (SIGSEGV)


1

Proste znaczenie Segmentation faultpolega na tym, że próbujesz uzyskać dostęp do pamięci, która nie należy do ciebie.Segmentation faultwystępuje, gdy próbujemy czytać i / lub pisać zadania w miejscu pamięci tylko do odczytu lub próbujemy zwolnić pamięć. Innymi słowy, możemy to wyjaśnić jako pewnego rodzaju uszkodzenie pamięci.

Poniżej wymienię typowe błędy popełniane przez programistów, które do nich prowadzą Segmentation fault.

  • Używaj scanf()w niewłaściwy sposób (zapomniałem umieścić &).
int num;
scanf("%d", num);// must use &num instead of num
  • Używaj wskaźników w niewłaściwy sposób.
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.
  • Modyfikacja literału łańcuchowego (wskaźnik próbuje zapisać lub zmodyfikować pamięć tylko do odczytu).
char *str;  

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

//Problem:  trying to modify read only memory
*(str+1) = 'n';
  • Spróbuj dotrzeć przez adres, który jest już zwolniony.
// 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; 
  • Przepełnienie stosu: zabrakło pamięci na stosie
  • Dostęp do tablicy poza granicami ”
  • Używaj złych specyfikatorów formatu podczas używania printf()i scanf()
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.