編寫安全代碼:不要在頭文件中定義變量

作者:[email protected]
博客:blog.focus-linux.net   linuxfocus.blog.chinaunix.net
 
微博:weibo.com/glinuxer
QQ技術羣:4367710
 
本文的copyleft歸[email protected]所有,使用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的位置。真正理解了這個過程,肯定不會犯本文中的這個錯誤。

頭文件:

//
//  comment.h

#ifndef comment_h
#define comment_h

static int ss = 100;

extern int F();

#endif


F()函數所在文件


//
//  comment.c
//

#include <stdio.h>
#include "comment.h"

int F()
{
    ss = 88;
    printf("%s ss = %d \n",__FUNCTION__,ss);
    return ss;
}

main.c 所在文件:

//
//  main.c
//  

#include <stdlib.h>
#include <stdio.h>
#include "comment.h"


int main()
{
    printf("[0]--->[%d]\n",ss);
    F();
    printf("[1]--->[%d]\n",ss);

    return 0;
}

輸出結果:

[0]--->[100]
F ss = 88 
[1]--->[100]



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章