logo

Помилка сегментації в C/C++

Помилки сегментації в C або C++ — це помилка, яка виникає, коли програма намагається отримати доступ до місця пам’яті, на доступ до якого вона не має дозволу. Як правило, ця помилка виникає, коли доступ до пам’яті порушується, і є типом загальної помилки захисту. Segfaults є абревіатурою для сегментації дефектів.

The дамп ядра відноситься до запису стану програми, тобто її ресурсів у пам'яті та процесорі. Спроба отримати доступ до неіснуючої пам’яті або пам’яті, яка використовується іншими процесами, також спричиняє помилку сегментації, яка призводить до дампа ядра.



Під час роботи програма має доступ до певних областей пам’яті. По-перше, стек використовується для зберігання локальних змінних для кожної функції. Крім того, він може мати пам’ять, виділену під час виконання та збережену в купі (нове в C++, і ви також можете почути, що це називається безкоштовний магазин ). Єдина пам’ять, до якої програма має доступ, це її власна пам’ять (згадана раніше). Помилка сегментації буде результатом будь-якого доступу за межами цього регіону.

Помилка сегментації — це певний тип помилки, спричинений доступом до пам’яті, яка не належить вам :

  • Коли фрагмент коду намагається виконати операцію читання та запису в місці пам’яті, доступному лише для читання, або звільненому блоці пам’яті, це називається помилкою сегментації.
  • Це помилка, яка вказує на пошкодження пам'яті.

Поширені сценарії помилок сегментації

У разі помилки сегментації програма намагається отримати доступ до пам’яті, доступ до якої не авторизований, або яка не існує. Ось деякі поширені сценарії, які можуть спричинити помилки сегментації:



  1. Зміна рядкового літералу
  2. Доступ до звільненої адреси
  3. Доступ до меж індексу поза масивом
  4. Неправильне використання scanf()
  5. Переповнення стека
  6. Розіменування неініціалізованого покажчика

1. Зміна рядкового літералу

Рядкові літерали зберігаються в розділі пам’яті лише для читання. Ось чому наведена нижче програма може аварійно завершувати роботу (видає помилку сегментації), оскільки рядок *(str+1) = ‘n’ намагається записати пам’ять лише для читання.

приклад:

C






// C program to demonstrate segmentation fault> // by modifying a string literal> #include> int> main()> {> >char>* str;> >// Stored in read only part of data segment //> >str =>'GfG'>;> >// Problem: trying to modify read only memory //> >*(str + 1) =>'n'>;> >return> 0;> }>

>

>

C++




// C++ program to demonstrate segmentation fault> // by modifying a string literal> #include> using> namespace> std;> int> main()> {> >char>* str;> >// Stored in read only part of data segment //> >str =>'GfG'>;> >// Problem: trying to modify read only memory //> >*(str + 1) =>'n'>;> >return> 0;> }>

>

>

Вихід

тайм-аут: контрольована команда вивантажила ядро

/bin/bash: рядок 1: 32 Тайм-аут помилки сегментації 15 с.

Зверніться до Зберігання для рядків у C для отримання додаткової інформації.

2. Доступ до звільненої адреси

Тут, у наведеному нижче коді, вказівник p розіменовується після звільнення блоку пам’яті, що не дозволено компілятором. Такі покажчики називають висячими покажчиками, і вони викликають помилки сегментів або ненормальне завершення програми під час виконання.

приклад:

C




// C program to demonstrate segmentation fault> // by Accessing an address that is freed> #include> #include> int> main(>void>)> {> >// allocating memory to p> >int>* p = (>int>*)>malloc>(8);> >*p = 100;> >// deallocated the space allocated to p> >free>(p);> >// core dump/segmentation fault> >// as now this statement is illegal> >*p = 110;> >printf>(>'%d'>, *p);> >return> 0;> }>

java bool до рядка

>

>

C++




// C++ program to demonstrate segmentation fault> // by Accessing an address that is freed> #include> using> namespace> std;> int> main(>void>)> {> >// allocating memory to p> >int>* p = (>int>*)>malloc>(>sizeof>(>int>));> >*p = 100;> >// deallocated the space allocated to p> >free>(p);> >// segmentation fault> >// as now this statement is illegal> >*p = 110;> >return> 0;> }>

>

>

Вихід

Segmentation Fault>

3. Доступ до індексу масиву поза межами

У C і C++ доступ до індексу масиву поза межами може спричинити помилку сегментації або іншу невизначену поведінку. У C і C++ немає перевірки меж для масивів. Хоча в C++ використання контейнерів, таких як метод std::vector::at() або оператор if(), може запобігти помилкам поза межами.

приклад:

C




// C program to demonstrate segmentation> // fault when array out of bound is accessed.> #include> int> main(>void>)> {> >int> arr[2];> >// Accessing out of bound> >arr[3] = 10;> >return> (0);> }>

>

>

C++




// C++ program to demonstrate segmentation> // fault when array out of bound is accessed.> #include> using> namespace> std;> int> main()> {> >int> arr[2];> >// Accessing out of bound> >arr[3] = 10;> >return> 0;> }>

>

>

Вихід

Segmentation Faults>

4. Неправильне використання scanf()

Функція scanf() очікує адресу змінної як вхідні дані. Тут, у цій програмі, n приймає значення 2 і припускає, що його адреса дорівнює 1000. Якщо ми передаємо n у scanf(), вхідні дані, отримані з STDIN, поміщаються в недійсну пам’ять 2, яка натомість має бути 1000. Це спричиняє пошкодження пам’яті, що призводить до помилки сегментації.

приклад:

C




// C program to demonstrate segmentation> // fault when value is passed to scanf> #include> int> main()> {> >int> n = 2;> >scanf>(>'%d'>, n);> >return> 0;> }>

>

>

C++




// C++ program to demonstrate segmentation> // fault when value is passed to scanf> #include> using> namespace> std;> int> main()> {> >int> n = 2;> >cin>> n;> >return> 0;> }>

linux free ipconfig
>

>

Вихід

Segementation Fault>

5. Переповнення стека

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

приклад:

C




// C program to illustrate the> // segmentation fault due to> // stack overflow> #include> int> main()> {> >int> arr[2000000000];> >return> 0;> }>

>

>

C++




// C++ program to illustrate> // the segmentation fault> // due to stack overflow> #include> using> namespace> std;> int> main()> {> >int> array[2000000000];> >return> 0;> }>

>

>

Вихід

Segmentation Fault>

6. Переповнення буфера

Якщо дані, що зберігаються в буфері, перевищують виділений розмір буфера, відбувається переповнення буфера, що призводить до помилки сегментації. Більшість методів у мові C не виконують перевірку прив’язки, тому переповнення буфера часто трапляється, коли ми забуваємо виділити необхідний розмір для буфера.

приклад:

C




// C program to illustrate the> // segementation fault due to> // buffer overflow> #include> int> main()> {> >char> ref[20] =>'This is a long string'>;> >char> buf[10];> >sscanf>(ref,>'%s'>, buf);> >return> 0;> }>

>

>

C++




// C++ program to illustrate the> // segementation fault due to> // buffer overflow> #include> using> namespace> std;> int> main()> {> >char> ref[20] =>'This is a long string'>;> >char> buf[10];> >sscanf>(ref,>'%s'>, buf);> >return> 0;> }>

>

>

Вихід

Segmentation Fault>

7. Розіменування неініціалізованого або NULL покажчика

Поширеною помилкою програмування є розіменування неініціалізованого вказівника (дикого вказівника), що може призвести до невизначеної поведінки. Коли вказівник використовується в контексті, який розглядає його як дійсний вказівник і отримує доступ до його основного значення, навіть якщо він не був ініціалізований для вказівки на дійсне розташування пам’яті, виникає ця помилка. Це може призвести до пошкодження даних, програмних помилок або помилок сегментації. Залежно від середовища та стану під час розіменування неініціалізовані покажчики можуть давати різні результати.

Як ми знаємо, вказівник NULL не вказує на жодну область пам’яті, тому його розіменування призведе до помилки сегментації.

приклад:

C




// C program to demonstrate segmentation> // fault when uninitialized pointer> // is accessed> #include> int> main()> {> >int>* ptr;> >int>* nptr = NULL;> >printf>(>'%d %d'>, *ptr, *nptr);> >return> 0;> }>

>

>

C++




// C++ program to demonstrate segmentation> // fault when uninitialized pointer> // is accessed> #include> using> namespace> std;> int> main()> {> >int>* ptr;> >int>* nptr = NULL;> >cout << *ptr <<>' '> << *nptr;> >return> 0;> }>

>

>

Вихід

Segmentation Fault>

Як виправити помилки сегментації?

Ми можемо виправити помилки сегментації, звернувши увагу на згадані причини:

  • Уникайте модифікації рядкових літералів.
  • Будьте обережні, використовуючи покажчики, оскільки вони є однією з найпоширеніших причин.
  • Враховуйте розмір буфера та стека перед збереженням даних, щоб уникнути переповнення буфера чи стека.
  • Перевірка меж перед доступом до елементів масиву.
  • Обережно використовуйте scanf() і printf(), щоб уникнути неправильних специфікаторів формату або переповнення буфера.

Загалом причиною помилки сегментації є доступ до пам’яті, яка вам не належить у цьому просторі. Поки ми уникаємо цього, ми можемо уникнути помилки сегментації. Якщо ви не можете знайти джерело помилки навіть після того, як це зробили, рекомендується використовувати налагоджувач, оскільки він безпосередньо веде до місця помилки в програмі.