最近在專案中遇到乙個很有意思的問題。簡單重現一下,大概是如下情況。
在檔案f1.cpp中定義字串
char str[16] = "abcdefg";
在其他檔案中,比如f2.cpp中使用extern進行一下宣告
extern char *str;
在對str進行操作時,比如最簡單的
void func()
這時候竟然出現了"段錯誤"segmentation fault
其實單抓出來我們都能發現問題,但是在乙個龐大的專案中,這種細枝末節並沒那麼顯眼(當然這個**不是我寫的,坑不是我挖的,我不管就是不是我的錯,逃)
上面的問題其實很簡單,在f2.cpp中str
被宣告為了char *
,而在str
被定義的f1.cpp中,str
的型別是char
。這就是問題所在了。
可能很多人會忽略這種小問題,或者"覺得char
和char*
有什麼區別?不都一樣用嗎?"
雖然大多數情況下,在同乙個檔案中對char
和char*
操作好像沒有什麼不同,
比如
char str[16] = "aaa";
cout << arr << endl;
//傳參等等
那是因為在同乙個檔案中,如果你對變數名操作,編譯器會直接取用其位址而讓你產生錯覺,不過我們也能看出端倪
char str[16] = "aaa";
char *pstr = str;
//32位
sizeof(str); //16
sizeof(pstr); //4
回到剛才的問題,那麼為什麼在同乙個檔案中很正常的操作到了跨檔案時就出現了異常呢?
其實很簡單,我們除錯一下就知道,這裡我用的是visualstudio,
在同乙個檔案中時
str 0x0034f988 "aaa" char[16]
pstr 0x0034f988 "aaa" char *
可以看到我們定義pstr時是直接正確的將str這個字串的位址給賦值給了pstr,這一切就很合理了。
這時候我們再回來看在不同檔案時的情況
str 0x64636261 "讀取字串時出錯" char*
位址一看就不對,字串常量值應該在唯讀常量區的,這麼高的位址肯定是錯的,但是為什麼會出現這個位址呢?仔細看一下,這個位址有點奇怪64 63 62 61?
看一下ascii表,16進製制對應的字元分別是d c b a
, 但是為什麼會取到這個值呢?
這裡就牽扯到編譯的問題了,編譯器在編譯檔案f1.cpp的時候,將str名字及其位址加入符號表;
在編譯檔案f2.cpp的時候,由於str
只是宣告,而符號表中確實能找到str
及其位址,編譯鏈結順利通過沒有任何問題。但是到了執行的時候,再f2.cpp中使用str
的名字進行定址,從而順利找到f1.cpp中str
的首位址,但是問題出現了,這裡我們宣告的str
型別是char *
,這個指標的值是什麼呢?
指標變數的位址有了,值當然就是這個位址開始取4個位元組的值了,結果順利取到x64636261
。在f2.cpp中把宣告型別改為extern char str;
就沒有任何問題了。
也許有人會問「既然如此,位址不應該是0x61626364嗎?」
關於這個問題的話,,因為我的機器是小端的
到這裡這個問題的緣由就解釋清楚了,也警示了我們平時開發過程中對細節的重視要加強,這裡再次重申
型別和*型別不是同一種型別!!!!!!!
型別轉換小記
首先我想說的就是string 和tostring 方法 首先呢,tostring 方法是幾乎每個物件都有的方法,但是對於不同的物件這個方法的功能是不一樣的 因為重寫了嘛 而string 方法時乙個全域性的方法,所以他的實現功能就是固定的。就像是mdn上面說的。string全域性物件是乙個用於字串或乙...
MySQL欄位型別小記
這裡先總結資料型別。mysql中的資料型別大的方面來分,可以分為 日期和時間 數值,以及字串。下面就分開來進行總結。1.日期和時間資料型別 mysql資料型別 含義date 3位元組,日期,格式 2014 09 18 time 3位元組,時間,格式 08 42 30 datetime 8位元組,日期...
CONST VOID 型別轉換小記
const void pconstvoid 1 首先用const cast轉換將const void 的const屬性去掉 void pchar const cast pconstvoid 2 用static cast給void 變數加上const屬性,並轉換成char const char p s...