使用sprintf時溢出怎麼會影響到變數的值?
最近在MSP430G2553(一款微控制器)上寫程序,用到了stdio.h里的sprintf函數,其實老發現有錯誤,後來單步調試發現當我運行sprintf函數第三次的,有幾個全局變數的值被改變了,第一感覺就是sprintf使什麼東西溢出了,覆蓋了原先的數據。我想問的問題是:
1、就是標題。2、遇到這種問題時想要不上知乎獨立解決這個問題(計算機工作原理?編譯原理?)需要哪些知識,往大家推薦點書或者名詞我索引索引。還有如果單純問問題1,那我就不會在知乎問了。給出詳細信息:我在另一個文件a.h中定義了
//模式枚舉體
typedef enum
{
ConstVolMode = 0,
ConstCurMode
}ModeDef;
//模式枚舉體
extern ModeDef ConstMode;
在a.c里我初始化為ModeDef ConstMode = ConstVolMode;
然後在另一個文件b.c中include了a.h
可是我運行的時候發現執行到一句的時候ConstMode 的值被改變成一個莫名其妙的量。。
如圖:當我執行到sprintf(Strline3, "輸出電流:%.3fA", ADC_FiltedValue[1]);時
我跳到了彙編內運行。單步跳到00D9DE處,此時ConstMode的值還是ConstVolMode,截了個圖,按下一步得到下圖。明顯ConstMode的值被改成了一個奇怪的值。
關於第一個問題,這其實只是 C 語言的基礎問題,查看相關文檔可解決。sprintf 本身不是安全的,它會無條件往第一個參數的地址寫數據,無論這塊數據空間是誰的。當然,在格式化串的時候也可能發生問題只是概率較低,目前想來可能有問題的就這兩處,首先當然你應當去找 Strline3 的責任。
關於第二個問題,其實用 Linux 的話,標準庫函數直接 man 就好了。不行去問問 stackoverflow 也是好的,順帶提高英語讀寫技能。
順便說一下:也許會有很多人說你這程序寫得不好,全局變數亂飛什麼的。這其實也是事實,只不過很多時候程序能用,沒太多人去糾結這些細節。但你現在程序有問題了,再來看的時候,會發現你這個函數裡面冒出來的變數好像全是全局量,這對你查問題其實非常不利。如果你的程序是運行在x86的windows的話,那麼sprintf用的是cdecl這個calling convention。sprintf要求第一個參數是緩衝區的指針,而且他在輸入的時候是不檢查緩衝區的大小的。在這裡推薦使用VC++的sprintf_s函數。如果你的StrlineX是一個數組,不管是全局變數也好,函數的局部變數也好(這個更危險),那StrlineX的「後面」肯定是別的變數了。於是如果你提供給sprintf函數的緩衝區的尺寸不夠,那他就會把你的東西慢慢覆蓋到你接下來的所有變數裡面。
也就是說,如果你這麼聲明的話:
char a[10];char b;char c;在某些編譯選項下面,a[10]==b, a[11]==c這種情況是會出現的。VC++在debug下面會在變數中間放(通常是12個位元組的)某些值,退出函數的時候他會檢查一下這些值是不是被改寫了,如果是證明你搞錯了什麼事情,就掛了。這是release沒事但是debug有事的其中一種常見原因——因為release通常沒有檢查,而且函數的本地變數用完即棄,剛好你也沒有寫得太遠。
1. 關於第一個問題:
ConstMode是個全局變數,它在進程的地址空間中屬於數據區。
對於sprintf函數,第一個參數是個指針,指向一個緩衝區,存入輸出的內容。結合你的程序邏輯和執行情況,排除ConstMode被並發地從其它程序流程修改,
那麼唯一的可能就是Strline3在緊臨數據區中ConstMode的地方,導致輸出到Strline3這個緩衝區後,超出限制,繼續覆蓋後續的本來屬於ConstMode的內存區域。(我猜想很有可能ConstMode和Strline3這兩個變數都是定義為全局變數,並且,在程序鏈接後,Strline3分配在ConstMode地址之前,導致了這種可能性,如果你能把程序定義這兩個變數的部分給出,更有助於分析)2. 關於第二個問題:
1.了解程序進程地址空間概念,知道全局變數,臨時變數的區別是如何體現在進程空間上的。 這對於了解緩衝區溢出,堆溢出的原理很重要。你這個程序的原因就是,緩衝區長度分配有限 ,這個緩衝區目前看很有可能是定義為全局變數(即在數據段),它的長度在程序開始時就固定 了,這就百分百可以觸發緩衝區溢出問題。這顯然就不是合理的做法,更合理的做法是根據要輸 出數據的長度,動態地在堆區域分配(malloc或new). 如果對進程地址空間有了概念,很多這種問題能避免,或者幫助分析。- 某一操作系統書籍中關於進程內存空間章節的描述,
- 編譯,鏈接的一些知識,《程序員的自我修養—鏈接、裝載與庫》是不錯的入門書
- 任一本彙編書籍中的關於函數部分的論述章節
《深入理解計算機系統》絕對是你想要的那一本:深入理解計算機系統(原書第2版) (豆瓣)
sprintf不會檢查緩衝區長度,在一些編譯選項下會造成超過長度bug,也就是你提出的問題,如果是windows下vc++的話,盡量用安全函數,sprintf_s
推薦閱讀:
※小弟做c++伺服器差不多一年了,用ace框架的,還沒什麼信心,還很菜,請教各位大神如何提升進階啊?
※計算機專業C++應該怎麼教?
※計算機中缺失MSVCP120D.dll和MSVCR120D.dll怎麼解決?
※多幀圖片的數據存儲的問題?
※從項目管理上來說,C++ 是否適合做大的項目?