嵌入式C語言(一)

一、關鍵字

很多的C語言教材都是這針對面向計算機編程,因此很多時候忽略了一些不常用的關鍵字的講解,而在嵌入式中往往會看到很多不常用的關鍵字,值得我們去進一步理解。

1、register和auto

在計算機中CPU的運算速度最快,現在都達到3GHZ左右,而相對應的存儲器速度卻相對慢很多,訪問速度最快的寄存器和緩存,由於其體積又大,不適合大容量的使用,所以只能二者相接合的方式來提高效率。程序代碼保存在內存中,當使用數據時,將其送到寄存器,讓CPU來訪問,使用完畢,送回內存保存。
register修飾符暗示編譯程序相應的變量爲寄存器變量將被頻繁地使用,如果可能的話,應將其保存在CPU的寄存器中,以加快其存儲速度。在使用寄存器變量時,請注意:

  1. 待聲明爲寄存器變量類型應該是CPU寄存器所能接受的類型,意味着寄存器變量是單個變量,變量長度應該小於等於寄存器長度(整形長度)
  2. 不能對寄存器變量使用取地址符“&”,因爲該變量沒有內存地址(不在內存上)
  3. 只有局部變量和形參可以作爲寄存器變量,全局變量不行
  4. 靜態變量不可以被定義爲寄存器變量
    auto的出現意味着,當前變量的作用域爲當前函數或代碼段的局部變量,意味着當前變量會在內存棧上進行分配。(一般情況下編譯器都忽略auto)

2、continue、break和return

**continue:**結束當前循環,開始下一輪循環
**break:**跳出當前循環
**return:**子程序返回語句,可返回值或不返回值
初學者往往容易犯的錯誤是搞不清continue和break的區別,continue只是結束當前一輪的循環然後進行下一輪循環(如果有)而break是直接退出整個循環。

3、extern與static

static可用於修飾變量和函數,修飾的變量又可以分爲局部變量和全局變量,他們都存在於內存的靜態區

  1. 靜態局部變量:存儲在靜態變量區,生命週期爲整個程序的生命週期,作用域只在對應的函數 內部,因此只有在對應的函數中才能夠修改,相對比較安全。
  2. 靜態全局變量:存儲在靜態變量區,生命週期爲整個程序的生命週期,作用域旨在對應的文件中,在其他文件中即使使用extern進行聲明也無法調用它。
  3. 靜態函數:限定了函數的作用域僅爲當前文件,好處是可以避免不同文件中的同名函數(一旦被定義爲靜態函數,就只能被同一文件內的函數所調用)
    extern:聲明外部定義,表示被定義的變量或函數爲一個已經在其他文件中定義的函數或變量,不再需要在該頁文件中重新定義。

4、volatile與const

volatile:易變變量聲明,與register作用正好相反,它的作用是放置編譯器將該變量定義爲一個寄存器變量,定義爲寄存器變量有時會帶來副作用即減少對內存的的讀寫,舉個例子
在選擇外部存儲芯片時我們 常需要一個引腳作爲片選信號CS,假如該引腳的定義沒有加volatile被編譯器錯誤的認爲可以變成寄存器變量就會發生以下的錯誤

int *Memory_CS=0x40ff000u;					//定義Memory_CS指向外接存儲器的片選引腳的輸出寄存器
*Memory_CS=0//低電平選中
.....
*Memory_CS=1;                                //重新拉高引腳

由於Memory_CS被編譯器錯誤的編譯爲了一個寄存器變量在兩次對0x40ff000地址寫的過程中並沒有進行讀取,編譯器就會認爲第一次的寫入是無效的從而忽略第一次的片選選擇。

const:只讀變量,一旦出現直接修改只讀變量,編譯器就會提示出錯。const的作用對象有其特有的規則“從左往右 忽略數據類型 看const修飾的是那個對象”,舉個例子

char * const P="abcd";        //定義一個字符指針P指向字符串abcd的起始地址    P爲常量
P[1]='1';					//修改P[1](*P)的內容允許,編譯器可以通過
P="abcd";					//重新改寫P所指向地址不允許,編譯器報錯

const char *P ="abcd";				//定義一個字符指針P指向字符串abcd的起始地址    *P爲常量
P[1]='1';					//修改P[1](*P)的內容不允許,編譯器報錯
P="abcd";					//重新改寫P所指向地址允許,編譯器通過

5、typedef

typedef:常用於爲複雜的聲明定義的別名,格式爲 typedef 標準聲明 別名; 例 typedef unsigned long uint32;
值得注意的是typedef常用語爲結構體例

typedef struct GpioMemMap
{
	uint32_t PDDR;							//Gpio 模塊中具體的寄存器分佈			
	.......
	uint32_t PDOR;
}	volatile * Gpio_MeMMapPtr;				//定義了一個Gpio模塊寄存器分佈結構體,併爲其起一個volatile類型的 別名指針變量

(Gpio_MeMMapPtr)0x400ff000u 相當於(volatile * GpioMemMap { …})0x400ff000u;

6、struct,enum與union

struct:結構體類型,在實際問題中常需要分配一整塊特定長度的內存空間,當該空間爲同一種數據類型時我們常使用數組類型來進行管理,但若該空間內部需要按不同的長度進行分配(如上面例子中Gpio模塊內部寄存器地址的分配),最好的方法就是結構體。格式如下

struct 結構體名
{
	結構體成員列表
} 結構體變量;
或
typedef struct 結構體名
{
	結構體成員列表
} 該結構體類型別名;

**enum:**枚舉名,計算機只能進行數據的計算,而日常生活中我們常遇到的問題往往與數字不是直接聯繫的,需要我們在他們之間建立起一種聯繫,這種聯繫我們就可以稱爲枚舉,列

enum week						
{
	monday = 0//在monday 和 0之間建立聯繫 在my_week類型變量中 monday 與0等價 ,賦值不是必須的 默認從0開始賦值
	tuesday=1//賦值不是必須的 , 默認爲前一個成員加一值
	.....
	sunday =6}  my_week;			//枚舉變量
my_week = monday;

同樣的如果你覺得反覆如此定義枚舉變量,也可以使用typedef起一個別名。
union:聯合體,定義和使用方式與結構體相類似,不同之處在於其內部成員變量共用同一塊內存空間。例

typedef union
{
	unsigned long DW;
	unsigned short W[2];
	unsigned char B[4];
}Dtype;
 int main ()
 {
	Dtype a;
	a.DW=0x100000000;
	printf("a.B[3]=0x%x\n",a.B[3]);
	return 0;		
}

結果爲 a.B[3]=0x10 從而判斷改平臺爲小端模式(高字節位於高地址)
同理訪問a.W也可以讀寫相同內存空間的內容

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