Что такое ошибка сегментации?
Что такое ошибка сегментации? Есть ли разница между языками C и C++ в этом контексте? Как ошибки сегментации связаны с висячими указателями?
5 ответ(ов)
Сегментация (segmentation fault) – это специфический вид ошибки, вызванной попыткой доступа к памяти, которая «вам не принадлежит». Это защитный механизм, который предотвращает повреждение памяти и возникновение сложных для отладки ошибок, связанных с памятью. Когда у вас происходит сегфолт, это сигнализирует о том, что вы делаете что-то неверное в работе с памятью – например, обращаетесь к переменной, которая уже была освобождена, или пытаетесь записать в область памяти, защищённую от записи и т.д. Сегментация по сути одинакова во многих языках, позволяющих управлять памятью, и нет принципиальных различий между сегфолтами в C и C++.
Существует множество способов вызвать сегфолт, особенно в языках низкого уровня, таких как C(++). Один из распространённых случаев – это разыменование нулевого указателя:
int *p = NULL;
*p = 1; // Здесь произойдёт сегфолт
Ещё одна ошибка происходит, когда вы пытаетесь записать в область памяти, отмеченную как доступная только для чтения:
char *str = "Foo"; // Компилятор помечает константную строку как доступную только для чтения
*str = 'b'; // Это недопустимо и приведёт к сегфолту
«Висячий» указатель (dangling pointer) указывает на объект, который больше не существует, как в следующем примере:
char *p = NULL;
{
char c;
p = &c; // p указывает на c
}
// Теперь p стал висячим
Указатель p
становится висячим, потому что он указывает на переменную c
, которая прекратила своё существование после завершения блока. И когда вы пытаетесь разыменовать висячий указатель (например, *p='A'
), вы, вероятно, получите сегфолт.
Следует отметить, что ошибка сегментации (segmentation fault) не вызывается прямым доступом к памяти другого процесса (что я иногда слышу), так как это просто невозможно. Виртуальная память предоставляет каждому процессу собственное виртуальное адресное пространство, и нет способа получить доступ к пространству другого процесса, используя любое значение указателя. Исключение составляют общие библиотеки, которые имеют одно и то же физическое адресное пространство, отображенное по (возможно) разным виртуальным адресам, и память ядра, которая вообще отображается одинаково в каждом процессе (по-моему, чтобы избежать сброса кэша TLB при системных вызовах). Также есть такие вещи, как shmat 😉, которые я считаю «косвенным» доступом. Тем не менее, можно проверить, что они обычно находятся далеко от кода процесса, и мы, как правило, можем получить к ним доступ (поэтому они там и есть), но неправильный доступ к ним приведет к ошибке сегментации.
Ошибка сегментации может также возникнуть при попытке доступа к собственной (процессорной) памяти неправильным образом (например, при попытке записи в пространство, которое нельзя записывать). Но наиболее распространенной причиной этой ошибки является доступ к части виртуального адресного пространства, которая вообще не отображается на физическое.
И все это в контексте систем виртуальной памяти.
Сегментация (segmentation fault) возникает, когда процесс пытается обратиться к странице, которая не указана в его таблице дескрипторов, или делает недопустимый запрос к странице, которая в таблице есть (например, попытка записи в страницу, доступную только для чтения).
Ошибочный указатель (dangling pointer) — это указатель, который может указывать на действительную страницу, но в то же время указывает на «неожиданную» область памяти.
Ошибка сегментации возникает, когда процесс (работающая экземпляр программы) пытается получить доступ к адресу памяти, который является только для чтения, либо к области памяти, используемой другим процессом, или пытается обратиться к несуществующему (недопустимому) адресу памяти.
Проблема висячей ссылки (указателя) означает, что происходит попытка доступа к объекту или переменной, содержимое которых уже было удалено из памяти. Например:
int *arr = new int[20];
delete arr;
cout << arr[1]; // проблема висячей ссылки возникает здесь
В этом примере после вызова delete arr;
память, на которую указывает arr
, становится невалидной, и попытка доступа к arr[1]
приводит к неопределённому поведению.
Простыми словами: ошибка сегментации — это сигнал от операционной системы к программе, который указывает на то, что было обнаружено illegal access к памяти. В результате, программа завершает свою работу преждевременно, чтобы предотвратить повреждение памяти.
В чем разница между #include <filename> и #include "filename"?
Как изменить цвет вывода echo в Linux
Каково влияние extern "C" в C++?
Разница между const int*, const int * const и int * const?
Является ли < быстрее, чем <=?