Что такое ошибка сегментации?


599

Что такое ошибка сегментации? Различен ли он в C и C ++? Как связаны ошибки сегментации и висячие указатели?


95
ошибка сегментации заставляет компилятор чувствовать себя плохо .
Бенджамин Крузье

22
Если это так, то почему в моем случае компилятор ничего не жаловал, все прошло гладко, но во время выполнения система выдает ошибку сегментации (дамп ядра)? T_T
Джим Рейнор

3
Просто дамп памяти, когда что-то идет не так!
Результаты

7
@pinouchon: Забавно, но когда компилятор имеет отношение к ошибкам сегмента? Разве это не скорее среда выполнения?
dhein

1
Обычно вызывается попыткой разыменования нулевого указателя, поэтому ошибка сегментации часто аналогична Java NullPointerException.
Raedwald

Ответы:


674

Ошибка сегментации - это особая ошибка, вызванная доступом к памяти, которая «не принадлежит вам». Это вспомогательный механизм, который не дает вам повредить память и вводить трудно исправляемые ошибки памяти. Всякий раз, когда вы получаете segfault, вы знаете, что делаете что-то не так с памятью - обращаетесь к переменной, которая уже была освобождена, записываете в доступную только для чтения часть памяти и т. Д. Ошибка сегментации практически одинакова в большинстве языков, которые позволяют вам связываться с В управлении памятью нет принципиальной разницы между ошибками в C и C ++.

Есть много способов получить segfault, по крайней мере на языках более низкого уровня, таких как C (++). Распространенный способ получить segfault - разыменовать нулевой указатель:

int *p = NULL;
*p = 1;

Другая ошибка происходит, когда вы пытаетесь записать часть памяти, которая была помечена как доступная только для чтения:

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

Свисающий указатель указывает на то, что больше не существует, как здесь:

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

Указатель pболтается, потому что он указывает на символьную переменную, cкоторая перестала существовать после окончания блока. И когда вы попытаетесь разыменовать висячий указатель (например *p='A'), вы, вероятно, получите segfault.


154
Последний пример особенно неприятен, когда я собираю: int main () {char * p = 0; {char c = 'x'; p = & c; } printf ("% c \ n", * p); вернуть 0; } С gcc или несколькими другими компиляторами он «работает». Нет предупреждений о компиляции. Нет сегфо Это потому, что '}' выходит за рамки, фактически не удаляет данные, просто помечает их как свободные для повторного использования. Код может хорошо работать в производственной системе в течение многих лет, вы изменяете другую часть кода, меняете компилятор или что-то еще и BOOOOOM!
Крис Хуан-Ливер

36
Извините за удар, но только примечание ... ни один из ваших примеров не обязательно вызывает segfault, на самом деле это просто неопределенное поведение ;-)
oldrinb

18
@oldrinb: Невозможно написать код, который обязательно вызывает ошибку. Не в последнюю очередь потому, что существуют системы, которые работают без защиты памяти, поэтому не могут определить, действительно ли часть памяти «принадлежит вам», и, таким образом , не знают о segfaults, только о неопределенном поведении ... (классический AmigaOS, например)
DevSolar

7
@ ChrisHuang-Leaver, вы должны понимать, что cэто локально, это означает, что он был помещен в стек после {и выскочил из него после }. висячий указатель - это просто ссылка на смещение, которое теперь находится вне стека. вот почему изменение его в простой программе никогда не вызовет никакого сбоя. с другой стороны, это может привести к segfault в более сложном случае, когда другие вызовы функций могут привести к росту стека и содержанию данных, на которые указывает свисающий указатель. запись в эти данные (локальные переменные) приведет к неопределенному поведению (segfault & Co)
Айман Хамума

3
@ ChrisHuang-Leaver, обычно, когда вы выходите из области видимости, компилятор должен восстановить некоторое пространство стека, чтобы освободить неиспользуемое пространство стека, но это не всегда происходит (с gcc, являющимся одним из этих компиляторов). Кроме того, выделенное пространство стека, как правило, снова используется повторно, поэтому я не слышал об операционных системах, которые возвращают неиспользуемые страницы стека в систему, что делает это пространство объектом для a SIGSEGV, поэтому я не ожидаю, что такой сигнал искажается стеком.
Луис Колорадо

111

Стоит отметить, что ошибка сегментации не вызвана прямым доступом к другой памяти процесса (это то, что я иногда слышу), поскольку это просто невозможно. С виртуальной памятью каждый процесс имеет свое собственное виртуальное адресное пространство, и нет никакого способа получить доступ к другому, используя любое значение указателя. Исключением из этого могут быть общие библиотеки, которые представляют собой одно и то же физическое адресное пространство, сопоставленное (возможно) разным виртуальным адресам и памяти ядра, которая даже отображается одинаково в каждом процессе (я думаю, что во избежание сброса TLB на syscall). И такие вещи, как shmat;) - это то, что я считаю «косвенным» доступом. Однако можно проверить, что они обычно расположены далеко от кода процесса, и мы обычно можем получить к ним доступ (вот почему они есть,

Тем не менее, ошибка сегментации может возникать в случае неправильного доступа к нашей собственной (процессной) памяти (например, при попытке записи в недоступное для записи пространство). Но наиболее распространенной причиной этого является доступ к той части виртуального адресного пространства, которая вообще не сопоставлена с физической.

И все это в отношении систем виртуальной памяти.


При использовании файлов с общей памятью / отображенной памятью кто-то другой может связываться с вашей памятью. В WIN32 также есть неприятные API, такие как WriteProcessMemory!
Пол

1
@Paulm: Да, я знаю. Это то, что я имел в виду в «И такие вещи, как шмат;) - это то, что я считаю« косвенным »доступом».
konrad.kruczynski

В операционной системе с виртуальной памятью нет никакого способа (обычно, пожалуйста, разработчики операционной системы, не обращайте на меня внимания) за процесс, чтобы получить доступ к виртуальной памяти другого процесса, не являясь каким-либо системным вызовом присоединения памяти, который позволяет вам доступ. Адреса виртуальной памяти обычно означают разные вещи в зависимости от рассматриваемого процесса.
Луис Колорадо

38

Ошибка сегментации вызвана запросом на страницу, которую процесс не указал в своей таблице дескрипторов, или неправильным запросом на страницу, которую он перечислил (например, запрос на запись на странице только для чтения).

Висячий указатель - это указатель, который может указывать или не указывать на допустимую страницу, но указывает на «неожиданный» сегмент памяти.


10
Это правда, но действительно ли это поможет вам, если вы уже не знаете, что такое ошибка сегментации?
Zoul

29

Честно говоря, как упоминали другие авторы, в Википедии есть очень хорошая статья на эту тему, так что загляните туда. Этот тип ошибки очень распространен и часто называется другими вещами, такими как нарушение прав доступа или общая ошибка защиты.

Они ничем не отличаются в C, C ++ или любом другом языке, который позволяет указатели. Такие ошибки обычно вызываются указателями, которые

  1. Используется перед правильной инициализацией
  2. Используется после того, как память, на которую они указывают, была восстановлена ​​или удалена.
  3. Используется в индексированном массиве, где индекс находится за пределами границ массива. Обычно это происходит только в том случае, если вы выполняете математическую обработку указателей на традиционных массивах или c-строках, а не на коллекциях на основе STL / Boost (в C ++.)

16

Согласно википедии:

Ошибка сегментации возникает, когда программа пытается получить доступ к области памяти, к которой у нее нет доступа, или пытается получить доступ к области памяти недопустимым способом (например, при попытке записи в область только для чтения или перезаписать часть операционной системы).


13

Ошибка сегментации также вызвана аппаратными сбоями, в данном случае памятью ОЗУ. Это менее распространенная причина, но если вы не нашли ошибку в своем коде, возможно, вам поможет memtest.

Решение в этом случае изменить оперативную память.

редактировать:

Здесь есть ссылка: Ошибка сегментации по аппаратному обеспечению


3
Быстрая и грязная проверка на наличие неисправного ОЗУ заключается в том, чтобы запускать программу сбоя снова и снова в цикле. Если у программы нет внутреннего недетерминизма, то есть она всегда выдает один и тот же вывод для одного и того же ввода, или, по крайней мере, так оно и должно быть, - но для какого-то конкретного ввода происходит сбой иногда , не всегда, но не никогда: тогда вам следует начать беспокоиться о плохой оперативной памяти.
Звол

8

Ошибка сегментации возникает, когда процесс (запущенный экземпляр программы) пытается получить доступ к адресу памяти только для чтения или диапазону памяти, который используется другим процессом, или получить доступ к несуществующему (недействительному) адресу памяти. Проблема с висячей ссылкой (указателем) означает, что при попытке доступа к объекту или переменной, содержимое которой уже удалено из памяти, например:

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

4
Правильный способ удаления массива - delete [] arr;
Дамиан

8

Страница Wikipedia Segmentation_fault содержит очень хорошее описание, просто указывая причины и причины. Загляните в вики для подробного описания.

В вычислительной технике ошибка сегментации (часто сокращенная до segfault) или нарушение доступа - это ошибка, возникающая от оборудования с защитой памяти, уведомляющего операционную систему (ОС) о нарушении доступа к памяти.

Ниже приведены некоторые типичные причины ошибки сегментации:

  • Разыменование указателей NULL - это специально для оборудования управления памятью
  • Попытка получить доступ к несуществующему адресу памяти (вне адресного пространства процесса)
  • Попытка доступа к памяти, на которую у программы нет прав (например, структуры ядра в контексте процесса)
  • Попытка записи в постоянную память (например, сегмент кода)

Это, в свою очередь, часто вызвано ошибками программирования, которые приводят к неправильному доступу к памяти:

  • Разыменование или присвоение неинициализированному указателю (дикий указатель, который указывает на случайный адрес памяти)

  • Разыменование или присвоение освобожденному указателю (висячий указатель, который указывает на память, которая была освобождена / освобождена / удалена)

  • Переполнение буфера.

  • Переполнение стека.

  • Попытка выполнить программу, которая не компилируется правильно. (Некоторые компиляторы выводят исполняемый файл, несмотря на наличие ошибок во время компиляции.)


6

Проще говоря: ошибка сегментации - это операционная система, посылающая программе сигнал о том, что она обнаружила несанкционированный доступ к памяти и преждевременно завершает работу программы, чтобы предотвратить повреждение памяти.


3

«Ошибка сегментации» означает, что вы пытались получить доступ к памяти, к которой у вас нет доступа.

Первая проблема с вашими аргументами главного. Основная функция должна быть int main(int argc, char *argv[]), и вы должны проверить, что argc по крайней мере 2, прежде чем получить доступ к argv [1].

Кроме того, поскольку вы передаете float в printf (который, кстати, преобразуется в double при переходе в printf), вы должны использовать спецификатор формата% f. Спецификатор формата% s предназначен для строк (символьные массивы, оканчивающиеся на \ 0).


2

Ошибка сегментации или нарушение доступа возникает, когда программа пытается получить доступ к ячейке памяти, которая не существует, или пытается получить доступ к ячейке памяти способом, который не разрешен.

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

Здесь я [1000] не существует, поэтому происходит ошибка.

Причины ошибки сегментации:

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
Прежде всего, ошибка seg не имеет ничего общего с адресом, который существует или не существует. Речь идет о том, что вы получаете доступ к нему там, где вам не разрешено это делать. И в вашем особом примере даже гарантируется, что это местоположение существует. так как стандарт говорит в случае массива, необходимо учитывать, что существует правильный адрес для указателя pointg на хорошо выровненном массиве в пределах его границ И 1 позади .
Dhein

он также высвобождается с адресом, если у вас нет адреса и если вы пытаетесь получить доступ к этому адресу, также присутствует seg. неисправность. И в моем примере это только для понимания.
Мохит Рохилла

2

В ответах есть несколько хороших объяснений «ошибки сегментации», но поскольку в случае ошибки сегментации часто возникает дамп содержимого памяти, я хотел бы поделиться тем, где взаимосвязь между частью «сбрасывать ядро» в ошибке сегментации (ядро сбрасывается) и память приходит от:

Примерно с 1955 по 1975 годы - до появления полупроводниковой памяти - доминирующая технология в компьютерной памяти использовала крошечные магнитные пончики, нанизанные на медные провода. Пончики были известны как «ферритовые сердечники», а основная память, таким образом, известна как «внутренняя память» или «ядро».

Взято отсюда .


2

Существует достаточно определений ошибок сегментации, я хотел бы привести несколько примеров, с которыми я сталкивался во время программирования, что может показаться глупой ошибкой, но потраченной впустую много времени.

  1. Вы можете получить ошибку сегментации в нижнем регистре, в то время как несоответствие типа аргумента в printf

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

вывод : Segmentation Fault (SIGSEGV)

  1. когда вы забыли выделить память для указателя, но пытаетесь его использовать.

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

вывод : Segmentation Fault (SIGSEGV)


1

Простой смысл в Segmentation faultтом, что вы пытаетесь получить доступ к памяти, которая не принадлежит вам. Segmentation faultпроисходит, когда мы пытаемся читать и / или записывать задачи в области памяти, доступной только для чтения, или пытаемся освободить память. Другими словами, мы можем объяснить это как своего рода повреждение памяти.

Ниже я упоминаю распространенные ошибки программистов, которые приводят к Segmentation fault.

  • Используйте scanf()неправильно (забыл поставить &).
int num;
scanf("%d", num);// must use &num instead of num
  • Используйте указатели неправильно.
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.
  • Изменение строкового литерала (указатель попытается записать или изменить доступную только для чтения память.)
char *str;  

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

//Problem:  trying to modify read only memory
*(str+1) = 'n';
  • Попробуйте связаться по адресу, который уже освобожден.
// 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; 
  • Переполнение стека -: нехватка памяти в стеке
  • Доступ к массиву за пределами »
  • Используйте неправильные спецификаторы формата при использовании printf()и scanf()'
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.