1.static關鍵字
要對static關鍵字深入瞭解,首先需要掌握標準C程式的組成。
標準C程式一直由下列部分組成:
1)本文段—CPU執行的機器指令部分,也就是你的程式。一個程式只有一個副本;唯讀,這是為了防止程式由於意外事故而修改自身指令;
2)初始化資料區段(資料區段)—在程式中所有賦了初值的全域變數,存放在這裡。
3)非初始化資料區段(bss段)—在程式中沒有初始化的全域變數;核心將此段初始化為0。
注意:只有全域變數被分配到資料區段中。
4)棧—增長方向:自頂向下增長;自動變數以及每次函數調用時所需要儲存的資訊(返回地址;環境資訊)。
5)堆——動態儲存裝置分配。
static在嵌入式C語言當中,它有三個作用:
作用一:在函數體,一個被聲明為靜態變數在這一函數被調用過程中維持其值不變。
作用二:在模組內(但在函數體外),一個被聲明為靜態變數可以被模組內所用函數訪問,但不能被模組外其它函數訪問。它是一個本地的全域變數。
作用三:在模組內,一個被聲明為靜態函數只可被這一模組內的其它函數調用。那就是,這個函數被限制在聲明它的模組的本地範圍內使用。
auto、register、extern和static
對應兩種儲存期:自動儲存期和靜態儲存期。
auto和register對應自動儲存期。具有自動儲存期的變數在進入聲明該變數的程式塊時被建立,它在該程式塊活動時存在,退出該程式塊時撤銷。
關鍵字extern和static用來說明具有靜態儲存期的變數和函數。
用static聲明的局部變數具有靜態儲存持續期(staticstorage duration),或靜態範圍(staticextent)。雖然他的值在函數調用之間保持有效,但是其名字的可視性仍限制在其局部域內。靜態局部對象在程式執行到該對象的聲明處時被首次初始化。
2.const 關鍵字
合理地使用關鍵字const可以使編譯器很自然地保護那些不希望被改變的參數,防止其被無意的代碼修改。
a.const關鍵字修飾的變數可以認為有唯讀屬性,但它絕不與常量劃等號。如下代碼:
const int i=5;
int j=0;
i=j;//非法,導致編譯錯誤,因為只能被讀
j=i; //合法
b.const關鍵字修飾的變數在聲明時必須進行初始化。如下代碼:
const int i=5; //合法
const intj;//非法,導致編譯錯誤
c.用const聲明的變數雖然增加了分配空間,但是可以保證型別安全。const最初是從C++變化得來的,它可以替代define來定義常量。在舊版本(標準前)的c中,如果想建立一個常量,必須使用前置處理器:
#define PI 3.14159
此後無論在何處使用PI,都會被前置處理器以3.14159替代。編譯器不對PI進行類型檢查,也就是說可以不受限制的建立宏並用它來替代值,如果使用不慎,很可能由預先處理引入錯誤,這些錯誤往往很難發現。而且,我們也不能得到PI的地址(即不能向PI傳遞指標和引用)。
const的出現,比較好的解決了上述問題。
d.C標準中,const定義的常量是全域的。
e.必須明白下面語句的含義,我自己是反覆記憶了許久才記住,方法是:若是想定義一個唯讀屬性的指標,那麼關鍵字const要放到‘*’後面。
char *const cp;//指標不可改變,但指向的內容可以改變
char const *pc1;//指標可以改變,但指向的內容不能改變
const char *pc2;//同上(後兩個聲明是等同的)
f.將函數傳入參數聲明為const,以指明使用這種參數僅僅是為了效率的原因,而不是想讓調用函數能夠修改對象的值。參數const通常用於參數為指標或引用的情況,且只能修飾輸入參數;若輸入參數採用“值傳遞”方式,由於函數將自動產生臨時變數用於複製該參數,該參數本就不需要保護,所以不用const修飾。例子:
void fun0(const int * a);
void fun1(const int &a);
調用函數的時候,用相應的變數初始化const常量,則在函數體中,按照const所修飾的部分進行常量化,如形參為constint * a,則不能對傳遞進來的指標所指向的內容進行改變,保護了原指標所指向的內容;如形參為constint & a,則不能對傳遞進來的引用對象進行改變,保護了原對象的屬性。
g.修飾函數傳回值,可以阻止使用者修改傳回值。(在嵌入式C中一般不用,主要用於C++)
h.const消除了前置處理器的值替代的不良影響,並且提供了良好的類型檢查形式和安全性,在可能的地方儘可能的使用const對我們的編程有很大的協助,前提是:你對const有了足夠的理解。
最後,舉兩個常用的標準C庫函式宣告,它們都是使用const的典範。
(1).字串拷貝函數:char*strcpy(char *strDest,const char *strSrc);
(2).返回字串長度函數:intstrlen(const char *str);
3. volatile關鍵字
一個定義為volatile的變數是說這變數可能會被意想不到地改變,這樣,編譯器就不會去假設這個變數的值了。精確地說就是,最佳化器在用到這個變數時必須每次都小心地重新讀取這變數的值,而不是使用儲存在寄存器裡的備份。由於訪問寄存器的速度要快過RAM,所以編譯器一般都會作減少存取外部RAM的最佳化。
一般說來,volatile用在如下的幾個地方:
(1)、中斷服務程式中修改的供其它程式檢測的變數需要加volatile;
(2)、多任務環境下各任務間共用的標誌應該加volatile;
(3)、儲存空間映射的硬體寄存器通常也要加volatile說明,因為每次對它的讀寫都可能有不同意義;
不懂得volatile的內容將會帶來災難,這也是區分C語言和嵌入式C語言程式員的一個關鍵因素。為強調volatile的重要性,再次舉例分析:
代碼一:
int a,b,c;
//讀取I/O空間0x100連接埠的內容
a= inword(0x100);
b=a;
a=inword(0x100);
c=a;
代碼二:
volatile int a;
int a,b,c;
//讀取I/O空間0x100連接埠的內容
a= inword(0x100);
b=a;
a=inword(0x100)
c=a;
在上述例子中,代碼會被絕大多數編譯器最佳化為如下代碼:
a=inword(0x100)
b=a;
c=a;
這顯然與編寫者的目的不相符,會出現I/O空間0x100連接埠漏讀現象,若是增加volatile,像代碼二所示的那樣,最佳化器將不會最佳化掉任何代碼.從上面來看,volatile關鍵字是會降低編譯器最佳化力度的,但它保證了程式的正確性,所以在適合的地方使用關鍵字volatile是件考驗編程功底的事情.
4.struct與typedef關鍵字
面對一個人的大型C/C++程式時,只看其對struct的使用方式我們就可以對其編寫者的編程經驗進行評估。因為一個大型的C/C++程式,勢必要涉及一些(甚至大量)進行資料群組合的結構體,這些結構體可以將原本意義屬於一個整體的資料群組合在一起。從某種程度上來說,會不會用struct,怎樣用struct是區別一個開發人員是否具備豐富開發經曆的標誌。在網路通訊協定、通訊控制、嵌入式系統的C/C++編程中,我們經常要傳送的不是簡單的位元組流(char型數組),而是多種資料群組合起來的一個整體,其表現形式是一個結構體。經驗不足的開發人員往往將所有需要傳送的內容依順序儲存在char型數組中,通過指標位移的方法傳送網路報文等資訊。這樣做編程複雜,易出錯,而且一旦控制方式及通訊協定有所變化,程式就要進行非常細緻的修改。
用法:在C中定義一個結構體類型要用typedef:
typedef struct Student
{
int a;
}Stu;
於是在聲明變數的時候就可:Stustu1;
如果沒有typedef就必須用structStudent stu1;來聲明這裡的Stu實際上就是structStudent的別名。
另外這裡也可以不寫Student(於是也不能structStudent stu1;了)
typedef struct
{
int a;
Stu;
struct關鍵字的一個重要作用是它可以實現對資料的封裝,有一點點類似與C++的對象,可以將一些分散的特性對象化,這在編寫某些複雜程式時提供很大的方便性.