論Segmentation fault

剛開始學c的時候,最頭疼的事情是編譯老是通不過,鬱悶的要死,只要編譯經過了,就興奮的要死。如今,最頭疼的事情是什麼呢,編譯沒問題,可是程序跑的時候會出現Segmentation fault!c++

這個東西很是的惱人,在仍是菜鳥階段不會用gdb的時候,實在解決不了就放棄了,或者用一些對內存管理沒有要求的語言來曲線救國了,可是自從開始接觸了gdb這類的調試語句以後,發現了出現程序員

Segmentation fault的一些狀況,以及解決的方法,寫出來總結下:編程

 

Segmentation fault 本質就是內存錯誤,在c/c++語言裏,就是指針錯誤,這個錯誤產生的緣由是你錯誤的使用了內存,訪問了你不應訪問的東西,常見的可歸類以下:數組

1.數組越界,你定義了一個大小爲10的數組array[10],可是c是從0開始計數的,最後的數組元素是array[9],而後你去訪問了a[10],這樣就讀到了一塊不屬於你的內存,結果出現了Segmentation fault.函數

固然,這個是最簡單的錯誤。注意點就會解決。學習

2.指針的問題。指針本質就是一個數字,IA32下就是一個4字節32位的數字,相似0xAB0011FF這個樣子的,若是是x86-64位下的話,就是相似於0xAABBCCDDEEFF0011這種類型8字節64位的,這個數字表示spa

的是一個內存地址,計算機根據這塊內存地址找到內存爲此地址的內容,根據其類型讀取。這裏特別說一下NULL指針,在gdb裏,你能夠看到NULL實際上就是0x0,固然,前面有其類型,好比(void*) 0x0,這個樣子的,指針

因此,若是你解引用這個NULL指針,其實就是要求計算機去訪問內存爲0的地址,結果可想而知。固然,有些人以爲本身沒蠢到去解引用這個地址,固然,一般狀況下這個錯誤不是這麼直接的,可是本質確是同樣的,我調試

舉個具體的例子你感覺下:內存

void changept(int* cpt){

  cpt=0x48;

  printf("in function,the pointer is: %x\n",cpt);

}

int main(){

  int *s=0x20AB;

  *s=0x332A;

  printf("before calling function,the pointer is: %x\n",s);

  changept(s);

  printf("after calling function,the pointer is: %x\n",s);

  return 0;

}

能想像出答案是什麼嗎,在函數changept中,咱們改變了指針cpt 的值,注意是指針自己的值,或者說是這個指針須要指向的地址,可是在函數調用以後,指針顯示的地址仍是沒有改變,可是在函數調用中,咱們發現指針的值變了。這固然是由於c語言自己的一個特性,函數是傳值調用,你傳進去一個指針,注意這個指針是個數字,好比例子中的0x20ab,結果函數在調用時複製了這個值給局部變量(這個局部變量是同類型指針),而後局部變量得到了0x20ab這個地址,接下來讓局部變量去指向了新的地址0x48,函數調用結束後這個指針消失,可是原來的指針確仍是指向原來的值。好,問題就出在這裏,若是我要在函數中爲這個指針分配一個內存,好比cpt=(int*)malloc(sizeof(int)*10);注意,從堆上分配的內存讓這個局部指針指向了另外一個地址,做爲參數的指針實際上仍是懸空的,沒有指向新開闢的地址,若是程序員想用這個懸空指針(他可能認爲已經得到了內存)去訪問這個開僻的新內存,那麼實際上他訪問的仍是原來的內存,這樣就會形成指針的錯誤引用,形成Segmentation fault這樣的錯誤。因此,不要在函數中改變指針自己的值

解決上述問題的方法很簡單,把函數changept的參數改爲(int** cpt),*cpt=0x48,調用的時候就是changept(&s). 結果就正確了. 爲啥呢,這個很簡單,你傳進去的是存儲這個指針的內存地址,也是個數字,好比0x88, 0x88所指向的內存空間裏存儲着這個指針0x20AB,而後呢,局部變量得到了0x88這個地址,也指向了指針0x20ab,解引用0x88,獲取0x20ab的值,並改成0x48, 此時指向0x20ab的指針已經被修改了,因而函數調用後的結果也變了。這個時候,假如要爲原來指向0x20ab的指針申請了新的地址,那麼指針也就指向了新的地址,這個時候訪問這段開闢出的地址就沒有問題了。因此,要想改變指針的值,要給函數傳入這個指針的地址,或者說是指向指針的指針。

3. 數值類型,儘可能不要用unsigned這類無符號類型的整數,好比你申請一塊地址,malloc(sizeof(int)*a),若是a=-1的話(IA32 下是 補碼 表示0xFFFFFFFF,unsigned a 會致使這個數變成一個超大的數,固然這個數仍是0xFFFFFFFF,可是做爲無符號來解讀的話就是一個超大的數了,是2^32-1這樣一個數,堆上分配不出這麼大的空間,致使程序出現意想不到的問題。此外,若是你的程序在一個循環內,若是用無符號的數去和相似負數之類的比,可能產生死循環或者內存越界。

 

4.字符串,其實主要就是注意,若是你用strlen之類的函數,記得分配內存的時候要加1,由於strlen不包含‘\0'在內,不然還會產生越界,形成segmentation fault.

 

其實以上問題避免了,應該會杜絕90%以上的段錯誤,可是,更本質的是,你應該多學習計算機底層的機制,畢竟,c的強大就是它既有高級語言的便捷,也有底層語言的強大,而瞭解指針也就至關與掌握了c語言的核心,終將幫助你提升自身的編程修養!

相關文章
相關標籤/搜索