C語言

當前位置 /首頁/計算機/C語言/列表

嵌入式C語言常用的關鍵字

嵌入式C語言不可不用的關鍵字你瞭解多少呢?小編給大家彙總在一起了。一起來看看吧!

嵌入式C語言常用的關鍵字

  ic關鍵字

這個關鍵字的作用是餓很難強大的。

要對static關鍵字深入瞭解,首先需要掌握標準C程式的組成。

標準C程式一直由下列部分組成:

1)正文段——CPU執行的機器指令部分,也就是你的程式。一個程式只有一個副本;只讀,這是為了防止程式由於意外事故而修改自身指令;

2)初始化資料段(資料段)——在程式中所有賦了初值的全域性變數,存放在這裡。

3)非初始化資料段(bss段)——在程式中沒有初始化的全域性變數;核心將此段初始化為0。

注意:只有全域性變數被分配到資料段中。

4)棧——增長方向:自頂向下增長;自動變數以及每次函式呼叫時所需要儲存的資訊(返回地址;環境資訊)。這句很關鍵,常常有筆試題會問到什麼東西放到棧裡面就足以說明。

5)堆——動態儲存分配。

在嵌入式C語言當中,它有三個作用:

嵌入式C語言不可不用的關鍵字你瞭解多少呢?小編給大家彙總在一起了。一起來看看吧!

作用一:在函式體,一個被宣告為靜態的變數在這一函式被呼叫過程中維持其值不變。

這樣定義的變數稱為區域性靜態變數:在區域性變數之前加上關鍵字static,區域性變數就被定義成為一個區域性靜態變數。也就是上面的作用一中提到的在函式體內定義的變數。除了型別符外,若不加其它關鍵字修飾,預設都是區域性變數。比如以下程式碼:

void test1(void)

{

unsigned char a;

static unsigned char b;

a++;

b++;

}

在這個例子中,變數a是區域性變數,變數b為區域性靜態變數。作用一說明了區域性靜態變數b的特性:在函式體,一個被宣告為靜態的變數(也就是區域性靜態變數)在這一函式被呼叫過程中維持其值不變。這句話什麼意思呢?若是連續兩次呼叫上面的函式test1:

void main(void)

{

test1();

test1();

}

然後使程式暫停下來,讀取a和b的值,你會發現,a=1,b=2。怎麼回事呢,每次呼叫test1函式,區域性變數a都會重新初始化為0x00;然後執行a++;而區域性靜態變數在呼叫過程中卻能維持其值不變。

通常利用這個特性可以統計一個函式被呼叫的次數。

宣告函式的一個區域性變數,並設為static型別,作為一個計數器,這樣函式每次被呼叫的時候就可以進行計數。這是統計函式被呼叫次數的最好的辦法,因為這個變數是和函式息息相關的,而函式可能在多個不同的地方被呼叫,所以從呼叫者的角度來統計比較困難。程式碼如下:

void count();

int main()

{

int i;

for (i = 1; i <= 3; i++)

{

count();

{

return 0;

}

void count()

{

static num = 0;

num++;

printf(" I have been called %d",num,"times/n");

}

輸出結果為:

I have been called 1 times.

I have been called 2 times.

I have been called 3 times.

看一下區域性靜態變數的詳細特性,注意它的作用域。

1)記憶體中的位置:靜態儲存區

2)初始化:未經初始化的全域性靜態變數會被程式自動初始化為0(自動物件的值是任意的,除非他被顯示初始化)

3)作用域:作用域仍為區域性作用域,當定義它的函式或者語句塊結束的時候,作用域隨之結束。

注:當static用來修飾區域性變數的時候,它就改變了區域性變數的儲存位置,從原來的棧中存放改為靜態儲存區。但是區域性靜態變數在離開作用域之後,並沒有被銷燬,而是仍然駐留在記憶體當中,直到程式結束,只不過我們不能再對他進行訪問。

作用二:在模組內(但在函式體外),一個被宣告為靜態的變數可以被模組內所用函式訪問,但不能被模組外其它函式訪問。它是一個本地的全域性變數。

這樣定義的變數也稱為全域性靜態變數:在全域性變數之前加上關鍵字static,全域性變數就被定義成為一個全域性靜態變數。也就是上述作用二中提到的在模組內(但在函式體外)宣告的靜態變數。

定義全域性靜態變數的好處:

<1>不會被其他檔案所訪問,修改,是一個本地的區域性變數。

<2>其他檔案中可以使用相同名字的變數,不會發生衝突。

全域性變數的詳細特性,注意作用域,可以和區域性靜態變數相比較:

1)記憶體中的位置:靜態儲存區(靜態儲存區在整個程式執行期間都存在)

2)初始化:未經初始化的全域性靜態變數會被程式自動初始化為0(自動物件的值是任意的,除非他被顯示初始化)

3)作用域:全域性靜態變數在宣告他的檔案之外是不可見的。準確地講從定義之處開始到檔案結尾。

當static用來修飾全域性變數的時候,它就改變了全域性變數的作用域(在宣告他的檔案之外是不可見的),但是沒有改變它的存放位置,還是在靜態儲存區中。

作用三:在模組內,一個被宣告為靜態的函式只可被這一模組內的其它函式呼叫。那就是,這個函式被限制在宣告它的模組的本地範圍內使用。

這樣定義的函式也成為靜態函式:在函式的返回型別前加上關鍵字static,函式就被定義成為靜態函式。函式的定義和宣告預設情況下是extern的,但靜態函式只是在宣告他的檔案當中可見,不能被其他檔案所用。

定義靜態函式的好處:

<1> 其他檔案中可以定義相同名字的函式,不會發生衝突

<2> 靜態函式不能被其他檔案所用。它定義一個本地的函式。

這裡我一直強調資料和函式的本地化,這對於程式的結構甚至優化都有巨大的好處,更大的作用是,本地化的資料和函式能給人傳遞很多有用的資訊,能約束資料和函式的作用範圍。在C++的物件和類中非常注重的私有和公共資料/函式其實就是本地和全域性資料/函式的擴充套件,這也從側面反應了本地化資料/函式的優勢。

最後說一下儲存說明符,在標準C語言中,儲存說明符有以下幾類:

auto、register、extern和static

對應兩種儲存期:自動儲存期和靜態儲存期。

auto和register對應自動儲存期。具有自動儲存期的變數在進入宣告該變數的程式塊時被建立,它在該程式塊活動時存在,退出該程式塊時撤銷。

關鍵字extern和static用來說明具有靜態儲存期的變數和函式。用static宣告的區域性變數具有靜態儲存持續期(static storage duration),或靜態範圍(static extent)。雖然他的值在函式呼叫之間保持有效,但是其名字的可視性仍限制在其區域性域內。靜態區域性物件在程式執行到該物件的'宣告處時被首次初始化。

  2. const 關鍵字

const關鍵字也是一個優秀程式中經常用到的關鍵字。關鍵字const 的作用是為給讀你程式碼的人傳達非常有用的資訊,實際上,宣告一個引數為常量是為了告訴了使用者這個引數的應用目的。通過給優化器一些附加的資訊,使用關鍵字const 也許能產生更緊湊的程式碼。合理地使用關鍵字const 可以使編譯器很自然地保護那些不希望被改變的引數,防止其被無意的程式碼修改。簡而言之,這樣可以減少bug的出現。

深入理解const關鍵字,你必須知道:

a. const關鍵字修飾的變數可以認為有隻讀屬性,但它絕不與常量劃等號。

如下程式碼:

const int i=5;

int j=0;

...

i=j; //非法,導致編譯錯誤,因為只能被讀

j=i; //合法

b. const關鍵字修飾的變數在宣告時必須進行初始化。如下程式碼:

const int i=5; //合法

const int j; //非法,導致編譯錯誤

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所修飾的部分進行常量化,如形參為const int * a,則不能對傳遞進來的指標所指向的內容進行改變,保護了原指標所指向的內容;如形參為const int & a,則不能對傳遞進來的引用物件進行改變,保護了原物件的屬性。

g. 修飾函式返回值,可以阻止使用者修改返回值。(在嵌入式C中一般不用,主要用於C++)

h. const消除了前處理器的值替代的不良影響,並且提供了良好的型別檢查形式和安全性,在可能的地方儘可能的使用const對我們的程式設計有很大的幫助,前提是:你對const有了足夠的理解。

最後,舉兩個常用的標準C庫函式宣告,它們都是使用const的典範。

1.字串拷貝函式:char *strcpy(char *strDest,const char *strSrc);

2.返回字串長度函式:int strlen(const char *str);

  3. volatile關鍵字

一個定義為volatile 的變數是說這變數可能會被意想不到地改變,這樣,編譯器就不會去假設這個變數的值了。精確地說就是,優化器在用到這個變數時必須每次都小心地重新讀取這個變數的值,而不是使用儲存在暫存器裡的備份。

由於訪問暫存器的速度要快過RAM,所以編譯器一般都會作減少存取外部RAM的優化。比如:

static int i=0;

int main(void)

{

...

while (1)

{

if (i)

dosomething();

}

}

/* Interrupt service routine. */

void ISR_2(void)

{

i=1;

}

程式的本意是希望ISR_2中斷產生時,在main當中呼叫dosomething函式,但是,由於編譯器判斷在main函式裡面沒有修改過i,因此可能只執行一次對從i到某暫存器的讀操作,然後每次if判斷都只使用這個暫存器裡面的“i副本”,導致dosomething永遠也不會被呼叫。

如果將將變數加上volatile修飾,則編譯器保證對此變數的讀寫操作都不會被優化(肯定執行)。此例中i也應該如此說明。

一般說來,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是件考驗程式設計功底的事情.

  ct與typedef關鍵字

面對一個人的大型C/C++程式時,只看其對struct的使用情況我們就可以對其編寫者的程式設計經驗進行評估。因為一個大型的C/C++程式,勢必要涉及一些(甚至大量)進行資料組合的結構體,這些結構體可以將原本意義屬於一個整體的資料組合在一起。從某種程度上來說,會不會用struct,怎樣用struct是區別一個開發人員是否具備豐富開發經歷的標誌。

網路協議、通訊控制、嵌入式系統的C/C++程式設計中,我們經常要傳送的不是簡單的位元組流(char型陣列),而是多種資料組合起來的一個整體,其表現形式是一個結構體。

經驗不足的開發人員往往將所有需要傳送的內容依順序儲存在char型陣列中,通過指標偏移的方法傳送網路報文等資訊。這樣做程式設計複雜,易出錯,而且一旦控制方式及通訊協議有所變化,程式就要進行非常細緻的修改。

用法:

在C中定義一個結構體型別要用typedef:

typedef struct Student

{

int a;

}Stu;

於是在宣告變數的時候就可:Stu stu1;

如果沒有typedef就必須用struct Student stu1;來宣告

這裡的Stu實際上就是struct Student的別名。

另外這裡也可以不寫Student(於是也不能struct Student stu1;了)

typedef struct

{

int a;

}Stu;

struct關鍵字的一個總要作用是它可以實現對資料的封裝,有一點點類似與C++的物件,可以將一些分散的特性物件化,這在編寫某些複雜程式時提供很大的方便性.

比如編寫一個選單程式,你要知道本級選單的選單索引號、焦點在屏上是第幾項、顯示第一項對應的選單條目索引、選單文字內容、子選單索引、當前選單執行的功能操作。若是對上述條目單獨操作,那麼程式的複雜程度將會大到不可想象,若是選單層數少些還容易實現,一旦選單層數超出四層,呃~我就沒法形容了。若是有編寫過選單程式的朋友或許理解很深。這時候結構體struct就開始顯現它的威力了:

//結構體定義

typedef struct

{

unsigned char CurrentPanel;//本級選單的選單索引號

unsigned char ItemStartDisplay; //顯示第一項對應的選單條目索引

unsigned char FocusLine; //焦點在屏上是第幾項

}Menu_Statestruct;

typedef struct

{

unsigned char *MenuTxt; //選單文字內容

unsigned char MenuChildID;//子選單索引

void (*CurrentOperate)();//當前選單執行的功能操作

}MenuItemStruct;

typedef struct

{

MenuItemStruct *MenuPanelItem;

unsigned char MenuItemCount;

}MenuPanelStruct;

TAG標籤:關鍵字 語言 嵌入式 #