問題出現:
今天在測試程序的時候,程序直接給了一個Segmentation fault.這可不大好。於是就開始了苦逼的debug里程。
debug過程:
一開始,先需要定位錯誤出現在什么地方。於是,調用gdb,run。然后再重新測試。
gdb清晰的指出了問題所在的地方。

至少是一個好開始吧。
不過一看,傻眼了。直接報了是string析構時除了問題。這可如何是好,庫函數里頭出錯怎么調試呢。
手頭沒有debug模式編譯的lib,看來這條路走不通了。而且,一般來說,這種成熟的庫是不會出問題的。於是再仔細看自己的代碼。
這個函數中,我總共申請了4個string的變量。既然是局部變量析構導致的,那看起來,就只有一個string有嫌疑了。

看到這里又開心又郁悶。郁悶的是,這個看起來沒有什么問題啊。很正常。
於是接着翻,找我的getRules()函數。

這里是用了自己弄的一個共享內存的庫,從共享內存里獲取數據后,賦值給rules,然后返回。看起來也很正常。
我們都知道,c++里的string采用了寫時拷貝的技術。只有寫的時候,才會將內容拷貝過去,否則,多個string就是共享同一塊存儲字符串的空間。於是,就有理由想,是不是原來的數據被析構了,而rules仍然指向原來的內存,最后導致了析構失敗呢?
於是,改了句代碼,return rules =>return rules.substr();
再查看其c_str()所返回的地址的確不一樣了。

不過,就像結果顯示的那樣,還是掛了。
看來,和這個rules沒什么關系了。不過,程序的確是在析構這個rules出現了問題。
再冷靜下來,發現出現這個問題還有一個特征,那就是rules不是為空的時候。當rules=“”的時候就沒事。
於是,問題就定位到了

仔細一看,終於發現代碼中的問題了:我的tmprule開了512,但是卻在strncpy指定拷貝了1024。改之,代碼就跑順暢了。
問題反思:
1. 程序在調用string的析構函數出現了錯誤。
原因: strncpy在拷貝的時候,由於我指定了長度為1024,於是strncpy復制的時候就越界了。由於我的string是局部變量,聲明在tmprule之前。因此,strncpy就一路復制下去把string的數據全部破壞了。進而string在析構的時候,就全部亂了套。而且,即使string不出問題,這個函數的調用堆棧也完蛋了,程序肯定也不行了。
2. strncpy復制導致棧中數據被破壞
原因: 明顯的原因,是我只申請了512大小的數據,卻要求復制1024長度,於是,strncpy就一路復制下去了。可是,strncpy不是遇到'\0'就停止復制了么?我的rule長度不到20,怎么會導致失敗的。
帶着這個疑問,我去找了strncpy的源碼。看了源碼應該就能理解了。
1 char* __strncpy(char* dest, const char* src, size_t n) 2 { 3 char c; 4 char *s = dest; 5 if (n >= 4) 6 { 7 size_t n4 = n >> 2; 8 for (;;) 9 { 10 c = *src++; 11 *dest++ = c; 12 if (c == '\0') 13 break; 14 c = *src++; 15 *dest++ = c; 16 if (c == '\0') 17 break; 18 c = *src++; 19 *dest++ = c; 20 if (c == '\0') 21 break; 22 c = *src++; 23 *dest++ = c; 24 if (c == '\0') 25 break; 26 if (--n4 == 0) 27 goto last_chars; 28 } 29 n -= dest - s; 30 goto zero_fill; 31 } 32 last_chars: 33 n &= 3; 34 if (n == 0) 35 return dest; 36 for (;;) 37 { 38 c = *src++; 39 --n; 40 *dest++ = c; 41 if (c == '\0') 42 break; 43 if (n == 0) 44 return dest; 45 } 46 zero_fill: 47 while (n-- > 0) 48 dest[n] = '\0'; 49 return dest - 1; 50 }
這個是gnu的strncpy的實現。我們能清晰的看到,原來strncpy在復制的時候,在遇到'\0'時,先復制過去,然后很“負責任”的把dest剩下置為了0。於是,我們的函數的棧就全完蛋了。(這里得思考下函數調用時棧,局部變量的布局)。
總結:
今天遇到的問題看起來很神奇,string析構失敗了。實際上還是在使用時並沒有明確函數的特性。這個bug也害我浪費了2個小時。好在最后還是翻了出來。也算是一個教訓,下次使用strncpy一定會注意了。還有,源碼真是好東西。不過,對於gnu在設計的時候,增加了zero_fill還是不能理解,也許是為了增加安全性吧。
