亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

C語言嵌入式系統編程修煉之內存操作

2019-11-17 05:19:18
字體:
來源:轉載
供稿:網友
數據指針

   在嵌入式系統的編程中,經常要求在特定的內存單元讀寫內容,匯編有對應的MOV指令,而除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)。

   同理,若執行:

long int *p = (long int *)0xF000FF00;

   則p++(或++p)的結果等同于:p = p+sizeof(long int) ,而p-(或-p)的結果是p = p-sizeof(long 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

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

const int a;
int const a;
const int *a;
int * const a;
int const * a const;

   (1) 要害字const的作用是為給讀你代碼的人傳達非常有用的信息。例如,在函數的形參前添加const要害字意味著這個參數在函數體內不會被修改,屬于"輸入參數"。在有多個形參的時候,函數的調用者可以憑借參數前是否有const要害字,清楚的辨別哪些是輸入參數,哪些是可能的輸出參數。

   (2)合理地使用要害字const可以使編譯器很自然地保護那些不希望被改變的參數,防止其被無意的代碼修改,這樣可以減少bug的出現。

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

const int SIZE = 10;
char a[SIZE]; /* 非法:編譯階段不能用到變量 */

  要害字volatile

   C語言編譯器會對用戶書寫的代碼進行優化,譬如如下代碼:

int a,b,c;
a = inWord(0x100); /*讀取I/O空間0x100端口的內容存入a變量*/
b = a;
a = inWord (0x100); /*再次讀取I/O空間0x100端口的內容存入a變量*/
c = a;

   很可能被編譯器優化為:

int a,b,c;
a = inWord(0x100); /*讀取I/O空間0x100端口的內容存入a變量*/
b = a;
c = a;

   但是這樣的優化結果可能導致錯誤,假如I/O空間0x100端口的內容在執行第一次讀操作后被其它程序寫入新值,則其實第2次讀操作讀出的內容與第一次不同,b和c的值應該不同。在變量a的定義前加上volatile要害字可以防止編譯器的類似優化,正確的做法是:

volatile int a;

   volatile變量可能用于如下幾種情況:

   (1) 并行設備的硬件寄存器(如:狀態寄存器,例中的代碼屬于此類);

   (2) 一個中斷服務子程序中會訪問到的非自動變量(也就是全局變量);

   (3) 多線程應用中被幾個任務共享的變量。

  CPU字長與存儲器位寬不一致處理

   在背景篇中提到,本文特意選擇了一個與CPU字長不一致的存儲芯片,就是為了進行本節的討論,解決CPU字長與存儲器位寬不一致的情況。80186的字長為16,而NVRAM的位寬為8,在這種情況下,我們需要為NVRAM提供讀寫字節、字的接口,如下:

typedef unsigned char BYTE;
typedef unsigned int WORD;
/* 函數功能:讀NVRAM中字節
* 參數:wOffset,讀取位置相對NVRAM基地址的偏移
* 返回:讀取到的字節值
*/
extern BYTE ReadByteNVRAM(WORD wOffset)
{
  LPBYTE lpAddr = (BYTE*)(NVRAM + wOffset * 2); /* 為什么偏移要×2? */

  return *lpAddr;
}

/* 函數功能:讀NVRAM中字
* 參數:wOffset,讀取位置相對NVRAM基地址的偏移
* 返回:讀取到的字
*/
extern WORD ReadWordNVRAM(WORD wOffset)
{
  WORD wTmp = 0;
  LPBYTE lpAddr;
  /* 讀取高位字節 */
  lpAddr = (BYTE*)(NVRAM + wOffset * 2); /* 為什么偏移要×2? */
  wTmp += (*lpAddr)*256;
  /* 讀取低位字節 */
  lpAddr = (BYTE*)(NVRAM + (wOffset +1) * 2); /* 為什么偏移要×2? */
  wTmp += *lpAddr;
  return wTmp;
}

/* 函數功能:向NVRAM中寫一個字節
*參數:wOffset,寫入位置相對NVRAM基地址的偏移
* byData,欲寫入的字節
*/
extern void WriteByteNVRAM(WORD wOffset, BYTE byData)
{
  …
}

/* 函數功能:向NVRAM中寫一個字 */
*參數:wOffset,寫入位置相對NVRAM基地址的偏移
* wData,欲寫入的字
*/
extern void WriteWordNVRAM(WORD wOffset, WORD wData)
{
  …
}

   子貢問曰:Why偏移要乘以2?

   子曰:請看圖1,16位80186與8位NVRAM之間互連只能以地址線A1對其A0,CPU本身的A0與NVRAM不連接。因此,NVRAM的地址只能是偶數地址,故每次以0x10為單位前進!


C語言嵌入式系統編程修煉之內存操作
圖1 CPU與NVRAM地址線連接

   子貢再問:So why 80186的地址線A0不與NVRAM的A0連接?

   子曰:請看《IT論語》之《微機原理篇》,那里面講述了關于計算機組成的圣人之道。

  總結

   本篇主要講述了嵌入式系統C編程中內存操作的相關技巧。把握并深入理解關于數據指針、函數指針、動態申請內存、const及volatile要害字等的相關知識,是一個優秀的C語言程序設計師的基本要求。當我們已經牢固把握了上述技巧后,我們就已經學會了C語言的99%,因為C語言最精華的內涵皆在內存操作中體現。

   我們之所以在嵌入式系統中使用C語言進行程序設計,99%是因為其強大的內存操作能力!

   假如你愛編程,請你愛C語言;

   假如你愛C語言,請你愛指針;

   假如你愛指針,請你愛指針的指針!

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
韩国日本不卡在线| 亚洲女在线观看| 欧美成人中文字幕| 欧美精品videosex极品1| 日韩精品中文字| 国内精品模特av私拍在线观看| 4438全国亚洲精品在线观看视频| 国产精品久久久久久av下载红粉| 91精品国产综合久久香蕉最新版| 亚洲精品一区二区网址| 91啪国产在线| 亚洲欧美在线免费| 国产精品96久久久久久| 欧美性xxxxx极品娇小| 日韩成人在线观看| 欧美巨猛xxxx猛交黑人97人| 欧美性猛交xxxx乱大交| 欧美专区日韩视频| 亚洲尤物视频网| 在线播放国产一区二区三区| 国产亚洲欧洲在线| 久久久久久国产免费| 国产精品久久久久久久久久ktv| 亚洲天堂影视av| 日韩一级裸体免费视频| 国产一区二区三区精品久久久| 日韩美女中文字幕| 日韩av手机在线观看| 国内久久久精品| 亚洲国产精品成人一区二区| 亚洲mm色国产网站| 日韩中文字幕不卡视频| 91精品国产91久久久久久吃药| 国产欧美精品va在线观看| 精品久久久久久亚洲国产300| 精品亚洲va在线va天堂资源站| 欧美激情第三页| 亚洲国产高清自拍| 日韩在线观看你懂的| 久久国产精品99国产精| 97人洗澡人人免费公开视频碰碰碰| 亚洲福利视频在线| 国产视频精品久久久| 国产精品18久久久久久首页狼| 秋霞av国产精品一区| 亚洲专区国产精品| 久久久久久九九九| 亚洲欧美成人精品| 不卡av电影院| 性欧美办公室18xxxxhd| 亚洲第一av网| 亚洲xxxxx性| 成人精品视频久久久久| 精品国产依人香蕉在线精品| 国产精品69精品一区二区三区| 国产色婷婷国产综合在线理论片a| 亚洲黄色av女优在线观看| 国产91免费看片| 中文字幕欧美日韩va免费视频| 欧美日本高清一区| 亚洲美女动态图120秒| 久久国产精品久久久| 精品成人在线视频| 亚洲一区二区三区毛片| 人人做人人澡人人爽欧美| 日韩在线免费观看视频| 狠狠色噜噜狠狠狠狠97| 国产精品91视频| 久久免费视频网站| 亚洲激情小视频| 国产一区二区香蕉| 国产成人精品a视频一区www| 亚洲欧美日韩网| 久久精品电影一区二区| 欧美日韩国产91| 欧美精品videofree1080p| 国产91免费看片| 欧美极品在线视频| 日韩在线精品视频| 亚洲男人的天堂在线播放| 欧洲精品毛片网站| 中文字幕久久亚洲| 久久国产一区二区三区| 亚洲天堂2020| 久久精品国产69国产精品亚洲| 青青草原成人在线视频| 日韩女优人人人人射在线视频| 亚洲第一精品夜夜躁人人爽| 亚洲欧美中文字幕在线一区| 久久久久久久久久久成人| 色综合亚洲精品激情狠狠| 高清日韩电视剧大全免费播放在线观看| 91精品久久久久久久久| 尤物九九久久国产精品的特点| 国产精品一区二区久久| 日韩专区在线观看| 久久久久国产精品免费| 国产成人精品免高潮费视频| 国产精品爽黄69天堂a| 在线播放国产一区二区三区| 色yeye香蕉凹凸一区二区av| 国产欧美一区二区三区视频| 91欧美精品午夜性色福利在线| 992tv在线成人免费观看| 欧美午夜片在线免费观看| 自拍偷拍亚洲在线| 欧美日韩国产123| 久久免费成人精品视频| 亚洲欧美精品在线| 精品一区二区亚洲| 中文字幕久久精品| 精品日本高清在线播放| 欧美一性一乱一交一视频| 成人午夜黄色影院| 一个人看的www欧美| 欧美夜福利tv在线| 亚洲视频网站在线观看| 国产精品专区h在线观看| 日韩男女性生活视频| 久久久久久有精品国产| 亚洲国产高清高潮精品美女| 欧美午夜片欧美片在线观看| 国产主播欧美精品| 久久99久久99精品中文字幕| 久久久精品一区| 日韩精品免费综合视频在线播放| 久久久视频在线| 久久精品亚洲热| 亚洲欧美激情一区| 欧美国产日韩中文字幕在线| 国产精品爱啪在线线免费观看| 亚洲国产精品成人一区二区| 欧美日韩亚洲一区二区三区| 亚洲欧美日韩一区二区在线| 欧美亚洲免费电影| 亚洲女性裸体视频| 国产欧美日韩专区发布| 久久亚洲综合国产精品99麻豆精品福利| 亚洲一区二区三区xxx视频| 国产亚洲一级高清| 91久久夜色精品国产网站| 国产69精品99久久久久久宅男| 3344国产精品免费看| 精品成人在线视频| 国产一区二区三区三区在线观看| 91天堂在线视频| 4k岛国日韩精品**专区| 国产精品久久久久福利| 日产精品99久久久久久| 97不卡在线视频| 亚洲精品久久7777777| 欧美激情精品久久久久久黑人| 亚洲精品美女在线观看播放| 亚洲国产一区二区三区在线观看| 精品福利在线观看| 国产91精品黑色丝袜高跟鞋| 亚洲精品美女网站| 久久成人一区二区| 国产不卡精品视男人的天堂| 一二美女精品欧洲| 亚洲欧洲av一区二区| 欧美成人午夜免费视在线看片| 欧美一级电影在线| 在线日韩中文字幕|