1. 程式人生 > 實用技巧 >Segmentation Fault

Segmentation Fault

Segmentation fault in Linux

段錯誤應該就是訪問了不可訪問的記憶體,這個記憶體要麼是不存在的,要麼是受系統保護的。

  • SIGSEGV是在訪問記憶體時發生的錯誤,它屬於記憶體管理的範疇
  • SIGSEGV是一個使用者態的概念,是作業系統在使用者態程式錯誤訪問記憶體時所做出的處理
  • 當用戶態程式訪問(訪問表示讀、寫或執行)不允許訪問的記憶體時,產生SIGSEGV
  • 當用戶態程式以錯誤的方式訪問允許訪問的記憶體時,產生SIGSEGV
    使用者態程式地址空間,特指程式可以訪問的地址空間範圍。如果廣義的說,一個程序的地址空間應該包括核心空間部分,只是它不能訪問而已。

SIGSEGV產生的可能情況

SIGSEGV在很多時候是由於指標越界引起的,但並不是所有的指標越界都會引發SIGSEGV。一個越界的指標,如果不引用它,是不會引起SIGSEGV的。而即使引用了一個越界的指標,也不一定引起SIGSEGV。

錯誤的訪問型別引起

#include <stdio.h>
#include <stdlib.h>


int main()
{
    char* ch = "hello world";
    ch[1] = 'H';

    return 0;
}

上述程式編譯沒有問題,但是執行時彈出SIGSEGV。此例中,”hello world”作為一個常量字串,在編譯後會被放在.rodata

節(GCC),最後連結生成目標程式時.rodata節會被合併到text segment與程式碼段放在一起,故其所處記憶體區域是隻讀的。這就是錯誤的訪問型別引起的SIGSEGV。

訪問了不屬於程序地址空間的記憶體

#include <stdio.h>
#include <stdlib.h>


int main()
{
    int* p = (int*)0xC0000fff; 
    *p = 10; 

    return 0;
}

還有一種可能,往受到系統保護的記憶體地址寫資料,最常見的就是給一個指標以0地址:

#include <stdio.h>
#include <stdlib.h>


int main()
{
    int  i = 0; 
    scanf ("%d", i);  /* should be used &i */ 
    printf ("%d\n", i);

    return 0;
}

訪問了不存在的記憶體

#include <stdio.h>
#include <stdlib.h>


int main()
{
    int *p = NULL;
    *p = 1;

    return 0;
}

在實際情況中,此例中的空指標可能指向使用者態地址空間,但其所指向的頁面實際不存在。

記憶體越界,陣列越界,變數型別不一致等

#include <stdio.h>
#include <stdlib.h>


int main()
{
    char test[1]; 
    printf("%c", test[10]); 

    return 0;
}

試圖把一個整數按照字串的方式輸出

#include <stdio.h>
#include <stdlib.h>


int main()
{
    int b = 10; 
    printf("%s\n", b);

    return 0;
}

棧溢位了,有時SIGSEGV,有時卻啥都沒發生