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

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

20 ответов

Решение

Ошибка сегментации - это особая ошибка, вызванная доступом к памяти, которая "не принадлежит вам". Это вспомогательный механизм, который удерживает вас от повреждения памяти и внесения трудных для отладки ошибок памяти. Всякий раз, когда вы получаете 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.

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

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

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

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

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

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

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

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

В то время как ответ Зула объясняет, что такое ошибка сегментации, я обнаружил, что такого рода ошибки могут быть особенно трудно обнаружить, особенно если вы новичок в низкоуровневых языках, таких как C++ или C. Вот некоторые из распространенных способов получить Ошибка сегментации в вашей программе:

Неправильная строка управления форматом в printf или же scanf заявления

Строка управления форматом должна иметь одинаковое количество спецификаторов преобразования (%s, %d и т. д.) как printf или же scanf имеет аргументы для печати или чтения. То же самое относится и к fprintf а также fscanf,

Не используется & на аргументы scanf

функция scanf принимает в качестве аргументов строку управления форматом и адреса переменных, в которые она помещает данные, в которые она читает. & Оператор (address of) используется для предоставления адреса переменной.

Вне границ ссылки на массив

Убедитесь, что вы не нарушили границы ни одного массива, который вы используете; т. е. вы не подписали массив со значением, меньшим, чем индекс его самого низкого элемента или больше, чем индекс его самого высокого элемента. Valgrind может пригодиться, чтобы обнаружить такие ссылки - вы можете использовать valgrind с --tool=exp-sgcheck флаг.

Доступ к неинициализированным указателям

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

Неправильное использование & (адрес) и * (разыменование) операторов

Вы должны быть осторожны при их использовании, особенно при передаче параметров по ссылке / с помощью указателей.

Shell Limits

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

Отладка с использованием gdb

Вы можете использовать отладчик gdb чтобы просмотреть обратный след core файл выгружен вашей программой. Всякий раз, когда программы segfault, они обычно сбрасывают содержимое памяти во время сбоя в core файл (core dumped). Скомпилируйте вашу программу с -g флаг, беги в gdb и использовать bt (Трассировка).

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

В вычислениях ошибка сегментации или нарушение доступа - это ошибка или состояние отказа, вызванное аппаратным обеспечением с защитой памяти, уведомляющее операционную систему о том, что программное обеспечение пыталось получить доступ к ограниченной области памяти. -ВИКИПЕДИЯ

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

      #include <stdio.h>

int main(int argc, char *argv[]) {
    
    int A = 'asd';
    puts(A);
    
    return 0;
    
}

'asd' -> представляет собой цепочку символов, а не одиночный символ тип данных. Таким образом, сохранение его как char вызывает ошибку сегментации . Хранение некоторых данных в неправильном месте.

Хранение этого string или цепочка символов как единый char пытается вставить квадратный колышек в круглое отверстие.

Прервано из-за сигнала: ОШИБКА СЕГМЕНТАЦИИ (11)

Простое значение 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()'

Рассмотрим следующие фрагменты кода,

СНиПЕТ 1

int *number = NULL;
*number = 1;

СНиПЕТ 2

int *number = malloc(sizeof(int));
*number = 1;

Я предполагаю, что вы знаете значение функций: malloc() и sizeof() если вы задаете этот вопрос.

Теперь, когда это решено, SNIPPET 1 выдаст ошибку ошибки сегментации. в то время как СНиППЕТ 2 не будет.

Вот почему.

Первая строка первого фрагмента создает переменную (* число) для хранения адреса какой-либо другой переменной, но в этом случае она инициализируется значением NULL. с другой стороны, вторая строка второго фрагмента создает одну и ту же переменную (* число) для хранения адреса некоторого другого, и в этом случае ей дается адрес памяти (потому что malloc() - это функция в C/C++, которая возвращает адрес памяти компьютера)

Дело в том, что вы не можете наливать воду в таз, который не был куплен, ИЛИ таз, который был куплен, но не был разрешен для использования вами. Когда вы пытаетесь это сделать, компьютер получает предупреждение и выдает ошибку SegFault.

Вы должны сталкиваться с этой ошибкой только с языками, близкими к низкоуровневым, такими как C/C++. В других языках высокого уровня есть абстракция, которая гарантирует, что вы не сделаете эту ошибку.

Также важно понимать, что ошибка сегментации не зависит от языка.

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

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

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

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

  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)

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

Ошибка сегмента, когда тип не соответствует

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

 /* "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 process’s 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).

Ошибка сегментации (иногда известная как segfault) происходит, когда ваша программа пытается получить доступ к памяти, к которой ей не разрешен доступ. Другими словами, когда ваша программа пытается получить доступ к памяти, которая превышает границы, установленные операционной системой для вашей программы. И это обычное обстоятельство, которое приводит к сбою программ; это часто связано с файлом с именем core.

Память программ разделена на разные сегменты:

  • текстовый сегмент для программных инструкций
  • сегмент данных для переменных и массивов, определенных во время компиляции
  • сегмент стека для временных (или автоматических) переменных, определенных в подпрограммах и функциях
  • сегмент кучи для переменных, выделяемых во время выполнения функциями, такими как malloc (в C) и allocate (в Fortran).

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

* Вызов memset(), например, приведет к segfault программы:

      memset((char *)0x0, 1, 100);

* В трех приведенных ниже примерах показаны наиболее частые виды ошибок сегментации, связанных с массивами:

Случай А

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

Случай Б

      /* Illegal memory access if value of n is not in the range 0, 1, ... 999 */ 
int n; int foo[1000]; for (int i = 0; i < n ; i++) foo[i] = i;

Случай С

      /* Illegal memory access because no memory is allocated for foo2 */
float *foo, *foo2; foo = (float*)malloc(1000); foo2[0] = 1.0;
  • В случае A массив foo определен для index = 0, 1, 2, ... 999 . Однако на последней итерации цикла for программа пытается получить доступ к foo[1000] . Это приведет к segfault, если эта ячейка памяти находится за пределами сегмента памяти, где находится foo . Даже если это не вызывает segfault, это все равно ошибка.
  • В случае B целое число n может быть любым случайным значением. Как и в случае A, если он не находится в диапазоне 0, 1, ... 999 , это может вызвать ошибку сегментации. Так это или нет, это, безусловно, ошибка.
  • В случае C выделение памяти для переменной foo2 было пропущено, поэтому foo2 будет указывать на случайное место в памяти. Доступ к foo2[0] , скорее всего, приведет к segfault.

* Еще одна типичная проблема программирования, вызывающая segfault, — неправильное использование указателей. Функция C scanf(), например, требует адрес переменной в качестве второго параметра; следовательно, следующее наверняка приведет к сбою программы с ошибкой сегментации:

      int foo = 0; scanf("%d", foo); 
/* Note missing & sign ; correct usage would have been &foo */

Хотя переменная foo может быть создана в ячейке памяти 1000, предыдущий вызов функции попытается считать целочисленные значения в ячейку памяти 0 в соответствии с определением foo.

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

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

      char *ptr = NULL;
{
char c;
ptr = &c; //After the block is over, ptr will be a dangling pointer.
}

Когда блок завершается, область видимости переменной c истекает. Поскольку теперь он указывает на что-то, чего не существует, «ptr» станет оборванным указателем.

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

      char *str ="Testing Seg fault.";
*str= "I hate Seg fault :( ";

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

Ошибка может быть вызвана присвоением значения неинициализированному указателю.

      int main(){
int *ptr;
*ptr=1;
return 0;
}

выход

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

Другие вопросы по тегам