當前位置:才華齋>計算機>C語言>

嵌入式C語言記憶體操作技巧

C語言 閱讀(1.72W)

在嵌入式系統的程式設計中,常常要求在特定的記憶體單元讀寫內容,彙編有對應的MOV指令,而除C/C++以外的其它程式語言基本沒有直接訪問絕對地址的能力。嵌入式C語言高手煉成之記憶體操作是怎樣的?下面是相關的知識,歡迎閱讀。

嵌入式C語言記憶體操作技巧

在嵌入式系統的實際除錯中,多借助C語言指標所具有的對絕對地址單元內容的讀寫能力。以指標直接操作記憶體多發生在如下幾種情況:

(1) 某I/O晶片被定位在CPU的儲存空間而非I/O空間,而且暫存器對應於某特定地址;

(2) 兩個CPU之間以雙埠RAM通訊,CPU需要在雙埠RAM的特定單元(稱為mail box)書寫內容以在對方CPU產生中斷;

(3) 讀取在ROM或FLASH的特定單元所燒錄的漢字和英文字模。

  譬如:

unsigned char *p = (unsigned char *)0xF000FF00;

*p="11";

以上程式的意義為在絕對地址0xF0000+0xFF00(80186使用16位段地址和16位偏移地址)寫入11。

在使用絕對地址指標時,要注意指標自增自減操作的結果取決於指標指向的資料類別。上例中p++後的結果是p= 0xF000FF01,若p指向int,即:

int *p = (int *)0xF000FF00;

p++(或++p)的結果等同於:p = p+sizeof(int),而p-(或-p)的結果是p = p-sizeof(int)。

記住:CPU以位元組為單位編址,而C語言指標以指向的資料型別長度作自增和自減。理解這一點對於以指標直接操作記憶體是相當重要的。

函式指標

  首先要理解以下三個問題:

(1)C語言中函式名直接對應於函式生成的指令程式碼在記憶體中的地址,因此函式名可以直接賦給指向函式的指標;

(2)呼叫函式實際上等同於"調轉指令+引數傳遞處理+迴歸位置入棧",本質上最核心的操作是將函式生成的目標程式碼的首地址賦給CPU的PC暫存器;

(3)因為函式呼叫的本質是跳轉到某一個地址單元的code去執行,所以可以"呼叫"一個根本就不存在的.函式實體,暈?請往下看:

請拿出你可以獲得的任何一本大學《微型計算機原理》教材,書中講到,186 CPU啟動後跳轉至絕對地址0xFFFF0(對應C語言指標是0xF000FFF0,0xF000為段地址,0xFFF0為段內偏移)執行,請看下面的程式碼:

typedef void (*lpFunction) ( ); /* 定義一個無引數、無返回型別的 */

/* 函式指標型別 */

lpFunction lpReset = (lpFunction)0xF000FFF0; /* 定義一個函式指標,指向*/

/* CPU啟動後所執行第一條指令的位置 */

lpReset(); /* 呼叫函式 */

在以上的程式中,我們根本沒有看到任何一個函式實體,但是我們卻執行了這樣的函式呼叫:lpReset(),它實際上起到了"軟重啟"的作用,跳轉到CPU啟動後第一條要執行的指令的位置。

記住:函式無它,唯指令集合耳;你可以呼叫一個沒有函式體的函式,本質上只是換一個地址開始執行指令!

陣列vs.動態申請

在嵌入式系統中動態記憶體申請存在比一般系統程式設計時更嚴格的要求,這是因為嵌入式系統的記憶體空間往往是十分有限的,不經意的記憶體洩露會很快導致系統的崩潰。

所以一定要保證你的malloc和free成對出現,如果你寫出這樣的一段程式:

char * function(void)

{

char *p;

p = (char *)malloc(…);

if(p==NULL)

…;

… /* 一系列針對p的操作 */

return p;

}

在某處呼叫function(),用完function中動態申請的記憶體後將其free,如下:

char *q = function();

free(q);

上述程式碼明顯是不合理的,因為違反了malloc和free成對出現的原則,即"誰申請,就由誰釋放"原則。不滿足這個原則,會導致程式碼的耦合度增大,因為使用者在呼叫function函式時需要知道其內部細節!

正確的做法是在呼叫處申請記憶體,並傳入function函式,如下:

char *p="malloc"(…);

if(p==NULL)

…;

function(p);

free(p);

p="NULL";

而函式function則接收引數p,如下:

void function(char *p)

{

… /* 一系列針對p的操作 */

}

基本上,動態申請記憶體方式可以用較大的陣列替換。對於程式設計新手,筆者推薦你儘量採用陣列!嵌入式系統可以以博大的胸襟接收瑕疵,而無法"海納"錯誤。畢竟,以最笨的方式苦練神功的郭靖勝過機智聰明卻範政治錯誤走反革命道路的楊康。

給出原則:

(1)儘可能的選用陣列,陣列不能越界訪問(真理越過一步就是謬誤,陣列越過界限就光榮地成全了一個混亂的嵌入式系統);

(2)如果使用動態申請,則申請後一定要判斷是否申請成功了,並且malloc和free應成對出現!

const在C++語言中則包含了更豐富的含義,而在C語言中僅意味著:"只能讀的普通變數",可以稱其為"不能改變的變數"(這個說法似乎很拗口,但卻最準確的表達了C語言中const的本質),在編譯階段需要的常數仍然只能以#define巨集定義!故在C語言中如下程式是非法的:

關鍵字const

const意味著"只讀"。區別如下程式碼的功能非常重要,也是老生長嘆,如果你還不知道它們的區別,而且已經在程式界摸爬滾打多年,那隻能說這是一個悲哀:

const int a;

int const a;

const int *a;

int * const a;

int const * a const;

const int SIZE = 10;

char a[SIZE]; /* 非法:編譯階段不能用到變數 */

  關鍵字volatile

volatile變數可能用於如下幾種情況:

(1) 並行裝置的硬體暫存器(如:狀態暫存器,例中的程式碼屬於此類);

(2) 一箇中斷服務子程式中會訪問到的非自動變數(也就是全域性變數);

(3) 多執行緒應用中被幾個任務共享的變數。