編寫安全程式碼:不要在標頭檔案中定義變數

爆豆發表於2015-05-14
作者:gfree.wind@gmail.com
部落格:blog.focus-linux.net   linuxfocus.blog.chinaunix.net
 
微博:weibo.com/glinuxer
QQ技術群:4367710
 
本文的copyleft歸gfree.wind@gmail.com所有,使用GPL釋出,可以自由拷貝,轉載。但轉載請保持文件的完整性,註明原作者及原連結,嚴禁用於任何商業用途。

==========================================================================================================================================================

今天這個主題很簡單。但是這麼一個初級問題,居然真正發生在我的周圍。


前幾天幫助同事檢查一個問題。他這樣描述該現象:他定義了一個全域性變數,然後呼叫一個函式F修改了該全域性變數,但是退出函式時該全域性變數的值又被改了。我到他的除錯環境中,先檢視了一下現象。問題重現了。—— 我一直認為,所有能夠重現的問題都不是問題。只要能夠重現,就一定可以修正。


對於這一問題,我的第一反應是競爭引起的。於是我首先使用GDB的set scheduler-locking on,保證其它執行緒處於停止狀態,避免競爭。又試了一次,問題還是存在。在函式退出的時候,列印了一下當時的值。然後退出,再次列印,發現其值變為了初始值。感覺確實有點奇怪。於是看了看他的程式碼,一看該全域性變數定義在標頭檔案中static int g_variable = 0。看到這裡,儘管我不知道其它程式碼是怎麼寫的。我就想到了問題的原因。這裡的全域性變數g_variable肯定有兩份。函式F和呼叫者一定在不同的檔案中,它們都include了這個標頭檔案。結果在函式F中修改了一個g_variable,而呼叫者中使用和檢視的是另一個g_variable。解決方法是,去一個c檔案中定義這個全域性變數,然後到標頭檔案中宣告。


雖然我很快的解決了這個問題。但是我卻想,這個問題真的是一個很初級的問題。而我這位同事已經是一名senior的開發人員了。為什麼還會犯這種錯誤呢?這裡我對事不對人。主要的原因還是對於程式設計的基礎沒有理解。標頭檔案中不要定義全域性變數,看似是一條死的規則。其實只要真正領會什麼是標頭檔案,標頭檔案是如何include到.c原始檔中的。這條規則根本不需要記憶,而是一種理解。這樣會自然的就會寫出正確的程式碼,而不會犯這樣的錯誤。


說到這裡,簡單說一下標頭檔案的知識。標頭檔案的作用,主要是用於宣告變數,函式等等,然後可以被多個原始檔引用。其實我認為其根本目的,一是為了程式碼的整齊,更重要是為了消除重複的程式碼。因為多個原始檔都要相同的宣告,這時就可以用一行include 標頭檔案來解決。而include,在預編譯階段,實際上是將標頭檔案中所有的程式碼都插入到include的位置。真正理解了這個過程,肯定不會犯本文中的這個錯誤。


相關文章