當前位置:編程學習大全網 - 編程語言 - C51中static的用法

C51中static的用法

載選<編程思想>

非程序員 編 著

代碼永遠會有BUG,在這方面沒有最好只有更好。高效是程序員必須作到的事情,無錯是程序員壹生的追求。復用、分而治之、折衷是代碼哲學的基本思想。模塊化與面向對象是實現高效無錯代碼的方法。高效無錯代碼需要思想與實踐的不斷反復。

1.2.1 命名約定

命令規範基本上采用了微軟推薦的匈牙利命名法,略有簡化。

1. 常量

常量由大寫字母和數字組成,中間可以下劃線分隔,如 CPU_8051。

2. 變量

變量由小寫(變量類型)字母開頭,中間以大寫字母分隔,可以添加變量域前綴(變量活動域前綴以下劃線分隔)。如: v_nAcVolMin(交流電壓最小值)。

變量域前綴見下表

局部變量,如果變量名的含義十分明顯,則不加前綴,避免煩瑣。如用於循環的int型變量 i,j,k ;float 型的三維坐標(x,y,z)等。

3. 函數名壹般由大寫字母開頭,中間以大寫字母分隔,如SetSystemPara。函數命名采用動賓形式。如果函數為最底層,可以考慮用全部小寫,單詞間采用帶下劃線的形式。如底層圖形函數:pixel、lineto以及讀鍵盤函數get_key 等。

4. 符號名應該通用或者有具體含義,可讀性強。尤其是全局變量,靜態變量含義必須清晰。C++中的壹些關鍵詞不能作為符號名使用,如class、new、friend等。符號名長度小於31個,與ANSI C 保持壹致。命名只能用26個字母,10個數字,以及下劃線‘_’來組成,不要使用‘$’‘@’等符號。下劃線‘_’使用應該醒目,不能出現在符號的頭尾,只能出現在符號中間,且不要連續出現兩個。

5. 程序中少出現無意義的數字,常量盡量用宏替代。

1.2.2 使用斷言

程序壹般分為Debug版本和Release版本,Debug版本用於內部調試,Release版本發行給用戶使用。

斷言assert是僅在Debug版本起作用的宏,它用於檢查“不應該”發生的情況。以下是壹個內存復制程序,在運行過程中,如果assert的參數為假,那麽程序就會中止(壹般地還會出現提示對話,說明在什麽地方引發了assert)。

//復制不重疊的內存塊

void memcpy(void *pvTo, void *pvFrom, size_t size)

{

void *pbTo = (byte *) pvTo;

void *pbFrom = (byte *) pvFrom;

assert( pvTo != NULL && pvFrom != NULL );

while(size - - > 0 )

*pbTo + + = *pbFrom + + ;

return (pvTo);

}

assert不是壹個倉促拼湊起來的宏,為了不在程序的Debug版本和Release版本引起差別,assert不應該產生任何副作用。所以assert不是函數,而是宏。程序員可以把assert看成壹個在任何系統狀態下都可以安全使用的無害測試手段。

以下是使用斷言的幾個原則:

1)使用斷言捕捉不應該發生的非法情況。不要混淆非法情況與錯誤情況之間的區別,後者是必然存在的並且是壹定要作出處理的。

2)使用斷言對函數的參數進行確認。

3)在編寫函數時,要進行反復的考查,並且自問:“我打算做哪些假定?”壹旦確定了的假定,就要使用斷言對假定進行檢查。

4)壹般教科書都鼓勵程序員們進行防錯性的程序設計,但要記住這種編程風格會隱瞞錯誤。當進行防錯性編程時,如果“不可能發生”的事情的確發生了,則要使用斷言進行報警。

1.2.3 優化/效率

規則壹:對於在中斷函數/線程和外部函數中均使用的全局變量應用volatile定義。例如:

volatile int ticks;

void timer(void) interrupt 1 //中斷處理函數

{

ticks++

}

void wait(int interval)

{

tick=0;

while(tick<interval);

}

如果未用volatile,由於while循環是壹個空循環,編譯器優化後(編譯器並不知道此變量在中斷中使用)將會把循環優化為空操作!這就顯然不對了。

規則二:不要編寫壹條過分復雜的語句,緊湊的C++/C代碼並不見到能得到高效率的機器代碼,卻會降低程序的可理解性,程序出錯誤的幾率也會提高。

規則三:變量類型編程中應用原則:盡量采用小的類型(如果能夠不用“Float”就盡量不要去用)以及無符號Unsigned類型,因為符號運算耗費時間較長;同時函數返回值也盡量采用Unsigned類型,由此帶來另外壹個好處:避免不同類型數據比較運算帶來的隱性錯誤。

1.2.4 其他

規則壹:不要編寫集多種功能於壹身的函數,在函數的返回值中,不要將正常值和錯誤標誌混在壹起。

規則二:不要將BOOL值TRUE和FALSE對應於1和0進行編程。大多數編程語言將FALSE定義為0,任何非0值都是TRUE。Visual C++將TRUE定義為1,而Visual Basic則將TRUE定義為-1。例如:

BOOL flag;

if(flag) { // do something } // 正確的用法

if(flag==TRUE) { // do something } // 危險的用法

if(flag==1) { // do something } // 危險的用法

if(!flag) { // do something } // 正確的用法

if(flag==FALSE) { // do something } // 不合理的用法

if(flag==0) { // do something } // 不合理的用法

規則三:小心不要將“= =”寫成“=”,編譯器不會自動發現這種錯誤。

規則四:建議統壹函數返回值為無符號整形,0代表無錯誤,其他代表錯誤類型。

1.3 模塊化的C編程

C語言雖然不具備C++的面向對象的成分,但仍應該吸收面向對象的思想,采用模塊化編程思路。面向對象的思想與面向對象的語言是兩個概念。非面向對象的語言依然可以完成面向對象的編程,想想C++的誕生吧!

C++沒有理由對C存在傲慢與偏見,不是任何場合C++方法都是解決問題的良藥,譬如面對嵌入式系統效率和空間的雙重需求。註意我們談的是方法,而不是指編譯器。

C在軟件開發上存在的首要問題是缺乏對數據存取的控制(封裝),C編程者樂而不疲的使用著大量extern形式的全局變量在各模塊間交換著數據,“多方便啊”編程者樂曰,並傳授給下壹個編程者。這樣多個變量出現在多個模塊中,剪不斷理還亂,直到有壹天終於發現找壹個“人”好難。壹個東西好吃,智者淺嘗之改進之,而愚者只會直至撐死。

這世上本沒有什麽救世主,應在C上多下功夫,程序員和C締造者早就有過思考,相信野百合也有春天,還是看看C語言如何實現模塊化編程方法,在部分程度上具備了OO特性封裝與多態。

在具體闡述之前,需要明確生存期與可見性的概念。生存期指的是變量在內存的生存周期,可見性指的是變量在當前位置是否可用。兩者有緊密聯系,但不能混為壹談。壹個人存在但不可見只能解釋成上帝或靈魂,壹個變量存在但不可見卻並非咄咄怪事,模塊化方法正是利用了靜態函數、靜態變量這些“精靈”們特殊的生存期與可見性。

最後需要明確壹點的是這裏的模塊是以壹個.C文件為單位。

規則壹:利用函數命名規則和靜態函數

模塊中不被其他模塊調用的內部函數采用以下命名規則:用全部小寫,單詞間采用帶下劃線的形式。如底層圖形函數:pixel、lineto以及讀鍵盤函數get_key等。這些函數應定義為static靜態函數,這樣在其他模塊錯誤地調用這些函數時編譯器能給出錯誤(如BC編譯器)。(註意:有些編譯器不能報告錯誤,但為了代碼風格壹致和函數層次清晰,仍建議這樣作)。

規則二:利用靜態變量

模塊中不能被其他模塊讀寫的全局變量應采用static聲明,這樣在其他模塊錯誤地讀寫這些變量時編譯器能給出警告(C51編譯器)或錯誤(BC編譯器)。

規則三:引入OO接口概念和指針傳參

模塊間的數據接口(也就是函數)應該事先較充分考慮,需要哪些接口,通過接口需要操作哪些數據,盡量作到接口的不變性。

模塊間地數據交換盡量通過接口完成,方法是通過函數傳參數,為了保證程序高效和減少堆棧空間,傳大量參數(如結構)應采用傳址的方式,通過指針作為函數參數或函數返回指針,盡量杜絕extern形式的全局變量,請註意是extern形式的全局變量,模塊內部的全局變量是允許和必須的。

傳指針參數增加的開銷主要是作參數的指針和局部指針的數據空間(嵌入式系統(如C51)往往由於堆棧空間有限,函數參數會放到外部RAM的堆棧中),增加的代碼開銷僅是函數的調用,帶來的是良好的模塊化結構,而且使用接口函數會比在代碼中多處直接使用全局變量大大節約代碼空間。

需註意壹點的事物總有他的兩面性,水能載舟,也能覆舟。對於需要頻繁訪問的變量如果仍采用接口傳遞,函數調用的開銷是巨大的,這時應考慮仍采用extern全局變量。

以下演示了兩個C模塊交換數據:

//Module1.C

OneStruct* void GetOneStruct(void); //獲取模塊1數據接口

void SetOneStruct(OneStruct* pOneStruct); //寫模塊1數據接口

struct OneStruct

{

int m?_imember;

//……

}t1; //模塊1的數據

//t1初始化代碼…..

OneStruct* void GetOneStruct(void)

{

OneStruct* pt1; //只需定義壹個局部變量

t1.imember=15;

pt1=&t1;

return pt1;

}

void SetOneStruct(OneStruct* pOneStruct)

{

t1.imember=pOneStruct->imember;

//…….

}

//Module2.C

void OperateOneStruct(void); //模塊2通過模塊1提供的接口操作模塊1的數據

OneStruct* void GetOneStruct(void);

void SetOneStruct(OneStruct* pOneStruct);

void OperateOneStruct(void)

{

OneStruct* pt2; //只需定義壹個局部變量

pt2=GetOneStruct(); //讀取數據

SetOneStruct(pt2); //改寫數據

}

采用接口訪問數據可以避免壹些錯誤,因為函數返回值只能作右值,全局變量則不然。

例如 cOneChar == 4; 可能被誤為cOneChar = 4;

規則四:有限的封裝與多態

不要忘記C++的class源於C的struct,C++的虛函數機制實質是函數指針。為了使數據、方法能夠封裝在壹起,提高代碼的重用度,如對於壹些與硬件相關的數據結構,建議采用在數據結構中將訪問該數據結構的函數定義為結構內部的函數指針。這樣當硬件變化,需要重寫訪問該硬件的函數,只要將重寫的函數地址賦給該函數指針,高層代碼由於使用的是函數指針,所以完全不用動,實現代碼重用。而且該函數指針可以通過傳參數或全局變量的方式傳給高層代碼,比較方便。例如:

struct OneStruct

{

int m?_imember;

int (*func)(int,int);

//……

}t2;

  • 上一篇:信陽數控編程培訓五軸編程
  • 下一篇:植村秀小方瓶怎麽樣-植村秀小方瓶真假辨別
  • copyright 2024編程學習大全網