前幾天寫代碼因為自己的疏忽導致一遍又一遍的Segmentation fault (core dumped)。該問題是因為strerror(errno)返回的指針指向非法的內存導致程序打印錯誤時崩潰。
嘗試數次無果,為了進度。簡單粗暴的換成了perror(str)。今天忙裡偷閒,定位到了問題做個記錄和分享。
所有的坑,都是自己挖的。開始正題。
1 #include<stdio.h> 2 #include<errno.h> 3 //#include<string.h> 4 int main() 5 { 6 char *perr = NULL; 7 errno = 14; 8 perr = strerror(errno); 9 puts(perr); 10 return 0; 11 }
先看代碼,上述我屏蔽了#include<string.h>。如我所料,編譯通過(其實有warn已經告訴你有問題了),運行崩潰了。warn如下:
然後加上#include<string.h>程序完美編譯,完美成功執行。為什麼呢?經過與人(CSDN某某某)討論和浏覽stackoverflow上的帖子,以及我最終使用gcc -E預編譯 gcc -Wall的驗證。
我得到以下結論。
1.strerror()函數聲明在string.h頭文件裡(我以前以為是在errno.h裡的,還是基本功不行啊)。
2.gcc編譯時如果發現未定義的函數,它會認為該函數是定義在其他源文件中的,所以編譯是通過的。
但是因為編譯器看不見函數原型,所以它認為函數返回值為int。在鏈接階段,如果找到該函數則通過,找不到則報錯。
這裡我做了個實驗,如果函數原型在.o文件裡鏈接後沒有問題,連接器會修正返回值類型。但是如果鏈接的是.so文件,則返回值就是int。我也想不明白。
揭曉答案吧!我的程序為什麼會報錯呢?這是一系列疏忽大意加基本功不扎實的惡果。
因為我沒有包string.h。所以編譯器看不見函數原型,默認函數返回值為int。並給出了wara:assignment makes pointer from integer without a cast(用一個int型給指針賦值而沒有轉換--英文不好)。顯而易見,在64位系統中char*是64位的,int是32位。所以相當於我用32位的整型值當作地址賦值給了指針。所以指針指向非法內存。接著Segmentation fault (core dumped)就登場了。
系統中類型於strerror()這種返回指針的函數都可能有類似問題,所以頭文件該包的還是要包上。該注意的warn還是不容忽視啊。
文章到此已經結束,給個附錄。64位系統 gcc編譯器 各個數據類型長度表。
以上闡述由非權威人士撰寫。如有大牛深谙其理,歡迎跟帖深入說明。