c++ c++原因 - 什麼是分段錯誤?




dumped原因 segmentation (10)

什麼是分段錯誤? C和C ++有什麼不同? 分段錯誤和懸掛指針如何相關?


Answers

說實話,正如其他海報所說,維基百科有一個非常好的文章, 所以看看那裡。 這種類型的錯誤非常常見,常被稱為“訪問衝突”或“常規保護錯誤”等其他內容。

它們在C,C ++或任何允許指針的其他語言中沒有區別。 這些類型的錯誤通常是由指針引起的

  1. 在正確初始化之前使用
  2. 在它們指向的內存已經被釋放或刪除之後使用。
  3. 用於索引位於數組邊界之外的索引數組中。 這通常只在您對傳統數組或C字符串進行指針數學計算時,而不是基於STL / Boost的集合(使用C ++)。

雖然Zoul的答案解釋了什麼是分段錯誤,但我發現這些類型的錯誤可能特別難以捕捉,尤其是在您對C ++或C等低級語言不熟悉的情況下。這裡有一些常見的方法來獲取程序中的段錯誤:

printfscanf語句中的格式控製字符串不正確

格式控製字符串應該具有相同數量的轉換說明符( %s%d等),因為printfscanf具有要打印或讀取的參數。 這同樣適用於fprintffscanf

不使用&參數到scanf

函數scanf將格式控製字符串和變量的地址作為參數,在該變量中將放置它讀取的數據。 & (地址)運算符用於提供變量的地址。

超出邊界數組引用

確保你沒有違反你使用的任何數組的邊界; 也就是說,您沒有為數組下標數組,其值小於其最小元素的索引或大於其最高元素的索引。 Valgrind可以派上用場來檢測這種引用 - 你可以使用valgrind--tool=exp-sgcheck標誌。

訪問未初始化的指針

指針變量在被訪問前必須被分配一個有效的地址。 確保你已經初始化所有的指針指向一個有效的內存區域。

& (地址)和* (取消引用)操作符的使用不正確

使用這些時需要小心,特別是通過引用/使用指針傳遞參數時。

殼牌限制

有時,分段錯誤不是由程序中的錯誤引起的,而是由系統內存限制設置得太低而引起的。 通常這是堆棧大小的限制導致這種問題(堆棧溢出)。 要檢查內存限制,請在bash使用ulimit命令。

使用gdb調試

您可以使用調試器gdb來查看程序轉儲的core文件的回溯。 每當程序出現段錯誤時,他們通常會在崩潰時將內存內容轉儲到core文件中( core dumped )。 使用-g標誌編譯你的程序,在gdb運行並使用bt (backtrace)。


維基百科的Segmentation_fault頁面有一個非常好的描述,只是指出原因和原因。 查看維基的詳細描述。

在計算中,分段錯誤(通常縮寫為段錯誤)或訪問衝突是由具有內存保護的硬件引發的故障,通知操作系統(OS)關於內存訪問衝突。

以下是分段故障的一些典型原因:

  • 取消引用NULL指針 - 這是內存管理硬件的特殊功能
  • 嘗試訪問不存在的內存地址(外部進程的地址空間)
  • 試圖訪問內存時,程序沒有權限(例如進程上下文中的內核結構)
  • 試圖編寫只讀內存(如代碼段)

這些反過來又往往是由導致無效內存訪問的編程錯誤引起的:

  • 解引用或分配給未初始化的指針(野指針,指向隨機存儲器地址)

  • 解引用或分配給已釋放的指針(懸掛指針,指向已釋放/釋放/刪除的內存)

  • 緩衝區溢出。

  • 堆棧溢出。

  • 試圖執行無法正確編譯的程序。 (儘管存在編譯時錯誤,一些編譯器將輸出可執行文件。)


值得注意的是,分段錯誤不是由直接訪問另一個進程內存引起的(這是我有時聽到的),因為這是不可能的。 使用虛擬內存,每個進程都有自己的虛擬地址空間,並且無法使用任何指針值訪問另一個進程。 例外是共享庫,這些共享庫映射到(可能)不同的虛擬地址和內核內存,它們在每個進程中都以相同的方式映射(為了避免系統調用時TLB刷新,我認為)。 而像shmat這樣的東西) - 這些就是我所謂的“間接”訪問。 但是,可以檢查它們通常遠離流程代碼,我們通常能夠訪問它們(這就是為什麼它們在那裡,但以不正確的方式訪問它們將產生分段錯誤)。

但是,如果以不正確的方式訪問我們自己的(進程)內存(例如嘗試寫入不可寫入的空間),則會發生分段錯誤。 但最常見的原因是訪問虛擬地址空間的一部分,它根本沒有映射到物理地址空間。

而這一切都與虛擬內存系統有關。


當進程(程序的運行實例)試圖訪問其他進程正在使用的只讀內存地址或內存範圍或訪問不存在(無效)的內存地址時,會發生分段錯誤懸掛引用(指針)問題意味著試圖訪問內容已經從內存中刪除的對像或變量,例如:

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

簡而言之:分段錯誤是操作系統向程序發送一個信號,說明它檢測到非法的內存訪問並且過早地終止程序以防止內存被損壞。


根據維基百科:

如果程序試圖訪問不允許訪問的內存位置,或嘗試以不允許的方式訪問內存位置(例如,試圖寫入只讀位置或覆蓋部分操作系統)。


分段錯誤也是由硬件故障引起的,在這種情況下是RAM存儲器。 這是不太常見的原因,但是如果您在代碼中找不到錯誤,也許memtest可以幫助您。

在這種情況下的解決方案,更改RAM。

編輯:

這裡有一個參考: 硬件分割故障


分段錯誤是由進程未在其描述符表中列出的頁面請求引起的,也可能是它列出的頁面的無效請求(例如,在只讀頁面上的寫入請求)。

懸掛指針是一個指針,可能指向或不指向有效頁面,但會指向“意外”內存段。


我之前使用'extern“C”'來創建dll(動態鏈接庫)文件等main()函數“可導出”,以便稍後可以在dll的另一個可執行文件中使用它。 也許我曾經使用過它的一個例子會很有用。

DLL

#include <string.h>
#include <windows.h>

using namespace std;

#define DLL extern "C" __declspec(dllexport)
//I defined DLL for dllexport function
DLL main ()
{
    MessageBox(NULL,"Hi from DLL","DLL",MB_OK);
}

可執行程序

#include <string.h>
#include <windows.h>

using namespace std;

typedef LPVOID (WINAPI*Function)();//make a placeholder for function from dll
Function mainDLLFunc;//make a variable for function placeholder

int main()
{
    char winDir[MAX_PATH];//will hold path of above dll
    GetCurrentDirectory(sizeof(winDir),winDir);//dll is in same dir as exe
    strcat(winDir,"\\exmple.dll");//concentrate dll name with path
    HINSTANCE DLL = LoadLibrary(winDir);//load example dll
    if(DLL==NULL)
    {
        FreeLibrary((HMODULE)DLL);//if load fails exit
        return 0;
    }
    mainDLLFunc=(Function)GetProcAddress((HMODULE)DLL, "main");
    //defined variable is used to assign a function from dll
    //GetProcAddress is used to locate function with pre defined extern name "DLL"
    //and matcing function name
    if(mainDLLFunc==NULL)
    {
        FreeLibrary((HMODULE)DLL);//if it fails exit
        return 0;
    }
    mainDLLFunc();//run exported function 
    FreeLibrary((HMODULE)DLL);
}






c++ c segmentation-fault