1、什麼是段錯誤?
一旦一個程序發生了越界訪問,cpu 就會產生相應的保護,因而 segmentation fault 就出現了,經過上面的解釋,段錯誤應該就是訪問了不可訪問的內存,這個內存區要麼是不存在的,要麼是受到系統保護的,還有多是缺乏文件或者文件損壞。c++
2、段錯誤產生的緣由
下面是一些典型的段錯誤的緣由: 非關聯化空指針——這是特殊狀況由內存管理硬件 試圖訪問一個不存在的內存地址(在進程的地址空間) 試圖訪問內存的程序沒有權利(如內核結構流程上下文) 試圖寫入只讀存儲器(如代碼段)web
一、訪問不存在的內存地址
在C代碼,分割錯誤一般發生因爲指針的錯誤使用,特別是在C動態內存分配。非關聯化一個空指針老是致使段錯誤,但野指針和懸空指針指向的內存,可能會或可能不會存在,並且可能或不多是可讀的仍是可寫的,所以會致使瞬態錯誤。數組
#include <stdio.h> int main (void) { int *ptr = NULL; *ptr = 0; return 0; } 輸出結果: 段錯誤(核心已轉儲)
如今,非關聯化這些變量可能致使段錯誤:非關聯化空指針一般會致使段錯誤,閱讀時從野指針可能致使隨機數據但沒有段錯誤,和閱讀從懸空指針可能致使有效數據,而後隨機數據覆蓋。app
二、訪問系統保護的內存地址
#include <stdio.h> int main (void) { int *ptr = (int *)0; *ptr = 100; return 0; } 輸出結果: 段錯誤(核心已轉儲)
三、訪問只讀的內存地址
寫入只讀存儲器提出了一個 segmentation fault,這個發生在程序寫入本身的一部分代碼段或者是隻讀的數據段,這些都是由操做系統加載到只讀存儲器。優化
#include <stdio.h> #include <string.h> int main (void) { char *ptr = "test"; strcpy (ptr, "TEST"); return 0; } 輸出結果: 段錯誤(核心已轉儲)
#include <stdio.h> int main (void) { char *ptr = "hello"; *ptr = 'H'; return 0; } 輸出結果: 段錯誤(核心已轉儲)
上述例子ANSI C代碼一般會致使段錯誤和內存保護平臺。spa
它試圖修改一個字符串文字,這是根據ANSI C標準未定義的行爲。操作系統
大多數編譯器在編譯時不會抓,而是編譯這個可執行代碼,將崩潰。.net
包含這個代碼被編譯程序時,字符串「hello」位於rodata部分程序的可執行文件的只讀部分數據段。unix
當加載時,操做系統與其餘字符串和地方常數只讀段的內存中的數據。指針
當執行時,一個變量 ptr 設置爲指向字符串的位置,並試圖編寫一個H字符經過變量進入內存,致使段錯誤。
編譯程序的編譯器不檢查做業的只讀的位置在編譯時,和運行類unix操做系統產生如下運行時發生 segmentation fault。
#include <stdio.h> int main (void) { char ptr[] = "hello"; ptr[0] = 'H'; return 0; }
即便不能修改字符串(相反,這在C標準未定義行爲),在C char *類型,因此沒有隱式轉換原始代碼,在c++的 const char *類型,所以有一個隱式轉換,因此編譯器一般會抓住這個特定的錯誤。
四、空指針廢棄
由於是一個很常見的程序錯誤空指針廢棄(讀或寫在一個空指針,用於C的意思是「沒有對象指針」做爲一個錯誤指示器),大多數操做系統內存訪問空指針的地址,這樣它會致使段錯誤。
#include <stdio.h> int main (void) { int *ptr = NULL; printf ("%d\n", *ptr); return 0; } 輸出結果: 段錯誤(核心已轉儲)
這個示例代碼建立了一個空指針,而後試圖訪問它的值(讀值)。在運行時在許多操做系統中,這樣作會致使段錯誤。
非關聯化一個空指針,而後分配(寫一個值到一個不存在的目標)也一般會致使段錯誤。
#include <stdio.h> int main (void) { int *ptr = NULL; *ptr = 1; return 0; } 輸出結果: 段錯誤(核心已轉儲)
下面的代碼包含一個空指針,但當編譯一般不會致使段錯誤,值是未使用的。所以,廢棄一般會被優化掉,死代碼消除。
#include <stdio.h> int main (void) { int *ptr = NULL; *ptr; return 0; }
還有,好比malloc 動態分配內存,釋放、置空完成後,不可再使用該指針。
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { char* str=(char* )malloc(100); if(*str) { return; } strcpy(str,"hello"); printf("%s\n",str); free(str); str=NULL; strcpy(str,"abcdef"); return 0; } 輸出結果: hello 段錯誤 (核心已轉儲)
五、堆棧溢出
#include <stdio.h> #include <string.h> int main (void) { main (); return 0; } 輸出結果: 段錯誤(核心已轉儲)
上述例子的無限遞歸,致使的堆棧溢出會致使段錯誤,但無線遞歸未必致使堆棧溢出,優化執行的編譯器和代碼的確切結構。
在這種狀況下,高不可攀的代碼(返回語句)行爲是未定義的。
所以,編譯器能夠消除它,使用尾部調用優化,可能致使沒有堆棧使用。其餘優化可能包括將遞歸轉換成迭代,給出例子的結構功能永遠會致使程序運行,雖然可能不是其餘堆棧溢出。
六、內存越界(數組越界,變量類型不一致等)
#include <stdio.h> int main (void) { char test[10]; printf ("%c\n", test[100000]); return 0; } 輸出結果: 段錯誤(核心已轉儲)
轉自:
--------------------- 做者:聚優致成 來源:CSDN 原文:https://blog.csdn.net/qq_29350001/article/details/53780697