Что может привести к ошибкам сегментации в C ++?

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

Естественно, это сообщество Wiki, так как нет ни одного правильного ответа.

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

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

Виртуальная память (доступная для вас вся память = 2 ^ sizeof(pointer type) ) отображается в физическую память в единицах, называемых страницами или сегментами (пейджинговая замещенная сегментация, но они все еще используются).

На каждой странице есть некоторые права защиты, если вы попытаетесь прочитать со страницы без доступа к чтению, вы получите segfault. Если вы попытаетесь записать в исходное местоположение, вы получите SIGSEGV.

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

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

обработка ошибок страницы

Вас в основном интересует, что происходит в пользовательском пространстве и все пути, ведущие к SIGSEGV. но пространство ядра также интересно.

Выделение указателей NULL.

 #include  //For NULL. int* p1 = NULL; //p1 points to no memory address *p1 = 3; //Segfault. 

Доступ к массиву за пределы (возможно):

 int ia[10]; ia[10] = 4; // Someone forgot that arrays are 0-indexed! Possible Segfault. 

Многие из способов «segfault» C ++ не обязательно гарантированы , на самом деле, так обстоит дело с большинством приведенных здесь примеров. Это просто удача (или неудача, в зависимости от того, как вы ее смотрите!), Если вы можете выполнять эти операции без segfault.

На самом деле это одна из вещей на C ++, которая отделяет ее от других языков; неопределенное поведение. Если в Java или C # вы можете получить «InvalidOperationException» или подобное, что гарантировано произойдет, когда эти операции будут выполнены; в C ++ стандарт просто говорит «неопределенное поведение», что в основном является удачей ничьей, и вы никогда не хотите, чтобы это произошло.

Мой фаворит:

 #include  struct A { virtual void f() { std::cout << "A::f();\n"; } int i; }; struct B : A { virtual void f() { std::cout << "B::f();\n"; } int j; }; void seti(A* arr, size_t size) { for (size_t i = 0; i < size; ++i) arr[i].i = 0; } int main() { B b[10]; seti(b, 10); b[3].f(); } 

Как и в большинстве случаев, которые могут вызвать segfault, это также может не сработать. Например, на идеоне b[3].f() не работает, но b[2].f() работает.

Очевидным ответом является «неопределенное поведение», но это задает вопрос для неопытного программиста, и некоторые типы неопределенного поведения гораздо реже приводят к ошибке сегментации (или другого типа сбоя), чем другие. Наиболее частые причины ошибок сегментации, как правило, связаны с указателем: разыменование неинициализированного указателя, нулевой указатель или ранее освобожденный указатель; доступ за пределы (или перед началом, но это менее часто) объекта (массив или другой); используя результаты незаконного приведения указателя ( static_cast к производному типу, когда объект фактически не имеет этого типа, или большинство reinterpret_cast ); и т.п.

Однако, возможно, самым важным моментом здесь является то, что в общем случае они не гарантируют возникновения ошибки сегментации, и часто возникающая segmentation fault будет возникать когда-то позже, в совершенно не связанной с ней операции. Таким образом, запись за пределами локального массива обычно «работает», но будет изменять все, что происходит, чтобы следовать за массивом в стеке: некоторая другая локальная переменная (изменение vptr объекта в стеке может привести к ошибке сегментации, когда вы пытаетесь вызвать виртуальную функцию для объекта), указатель кадра вызывающей функции (который, вероятно, вызовет ошибку сегментации в этой функции после того, как вы вернетесь) или обратный адрес (который может вызвать всевозможные странные поведение – segmentation fault или незаконная ловушка команд, вероятно, являются лучшими, что может произойти). Запись за пределами освобожденной памяти или через уже освобожденный указатель может привести к повреждению арены свободного пространства, что приведет к сбою сегментации в значительно (когда-то много, много) позже распределении или бесплатном; он может также модифицировать какой-то другой, совершенно несвязанный объект, развращая его vptr или какой-либо другой указатель в объекте или только некоторые случайные данные – опять же, segmentation fault, вероятно, является наилучшим возможным результатом (гораздо предпочтительнее продолжать поврежденные данные).

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

 int* p1; //No initialization. *p1 = 3; //Possible segfault. 

Выделение освобожденной памяти может потенциально вызвать segfault.

 SomeClass* someObject = new SomeClass(); delete someObject; someObject->someMethod(); //Could cause a segfault. 

Попытка изменить строковые литералы:

 char* mystr = "test"; mystr[2] = 'w'; 

Это может привести к ошибке сегментации.

  • Ошибка сегментации в реализации btree
  • Почему java-приложение врезается в gdb, но работает нормально в реальной жизни?
  • Ошибка сегментации в strcpy ()
  • SegFault после scanf?
  • Segfaults в malloc () и malloc_consolidate ()
  • Давайте будем гением компьютера.