一级日韩免费大片,亚洲一区二区三区高清,性欧美乱妇高清come,久久婷婷国产麻豆91天堂,亚洲av无码a片在线观看

嵌入式開(kāi)發(fā)C語(yǔ)言問(wèn)題詳解

時(shí)間:2024-07-20 00:35:39 嵌入式培訓 我要投稿
  • 相關(guān)推薦

2016年嵌入式開(kāi)發(fā)C語(yǔ)言問(wèn)題詳解

  嵌入式系統的C語(yǔ)言開(kāi)發(fā)中,經(jīng)常遇到這樣那樣的問(wèn)題。有些問(wèn)題可能很快就能找到原因,但是有些問(wèn)題必須有一定的經(jīng)驗積累才能快速找到原因。

  yjbys小編整理了本人所了解的和經(jīng)常遇到的嵌入式開(kāi)發(fā)中的C語(yǔ)言典型問(wèn)題,不足之處歡迎各位專(zhuān)家指摘賜教。

  一、由編譯優(yōu)化引起的問(wèn)題

  例1、編譯后的匯編語(yǔ)言處理邏輯跟C語(yǔ)言處理邏輯不一致

  由于編譯器的原因,在設置了編譯優(yōu)化的情況下,編譯后有些代碼的邏輯會(huì )發(fā)生變化。這種情況下會(huì )發(fā)生很奇怪的問(wèn)題,一些函數的處理結果跟預想的不一致,而檢查代碼又看不出什么問(wèn)題。

  這種問(wèn)題的解決辦法一般是在充分分析軟件處理邏輯,確認處理上沒(méi)問(wèn)題后,把編譯后的列表文件(*.lis)和C語(yǔ)言處理邏輯逐行對照。把不一致的地方找出來(lái),并尋找修正對策。

  例2、編譯后的一些處理被優(yōu)化了

  這種問(wèn)題經(jīng)常發(fā)生在硬件寄存器的操作上。對于硬件而言,每一次讀寫(xiě)操作可能都有特定的含義:某些硬件寄存器要求讀一下才能做后續其他處理;而某些寄存器要連續寫(xiě)幾次。比如下面的情形:

  #define TSTREG (unsigned char *) 0x00C00032 /*TEST REGISTER */

  unsigned char *pTSTR;

  pTSTR = TSTREG;

  *pTSTR = 0x01; //這個(gè)操作很容易被編譯器優(yōu)化掉。

  *pTSTR = 0x02;

  ……

  作為對策之一,可以在定義變量時(shí)加上volatile關(guān)鍵字。比如:

  volatile unsigned char *pTSTR;

  volatile關(guān)鍵詞影響編譯器編譯的結果,用volatile聲明的變量表示該變量隨時(shí)可能發(fā)生變化,與該變量有關(guān)的運算,不要進(jìn)行編譯優(yōu)化,以免出錯。使用volatile變量的幾個(gè)場(chǎng)景:

  1)中斷服務(wù)程序中修改的供其它程序檢測的變量需要加volatile。

  2)多任務(wù)環(huán)境下各任務(wù)間共享的標志應該加volatile。

  3)存儲器映射的硬件寄存器通常也要加voliate,因為每次對它的讀寫(xiě)都可能有不同意義。

  二、由字節對齊引起的問(wèn)題

  一個(gè)結構體變量定義完之后,其在內存中的存儲并不一定等于其所包含元素的寬度之和。因為這里涉及到字節對齊的問(wèn)題。

  結構體中元素的對齊基本上遵循兩個(gè)基本原則:

  原則一:結構體中元素是按照定義順序一個(gè)一個(gè)放到內存中去的,但并不是緊密排列的。從結構體存儲的首地址開(kāi)始,每一個(gè)元素放置到內存中時(shí),它都會(huì )認為內存是以它自己的大小來(lái)劃分的,因此元素放置的位置一定會(huì )在自己寬度的整數倍上開(kāi)始(以結構體變量首地址為0計算)。

  原則二:在經(jīng)過(guò)第一原則分析后,檢查計算出的存儲單元是否為所有元素中最寬的元素的長(cháng)度的整數倍。若是,則結束;若不是,則補齊為它的整數倍。

  每個(gè)特定平臺上的編譯器都有自己的默認“對齊系數”(也叫對齊模數或邊界調整數)。通常,可以通過(guò)預編譯命令#pragma pack(n),n=1,2,4,8,16來(lái)改變這一系數,其中的n就是你要指定的“對齊系數”。

  #pragma pack(n) //編譯器將按照n字節對齊

  #pragma pack() //編譯器將取消自定義字節對齊方式

  在#pragma pack(n)和#pragma pack()之間的代碼按n字節對齊。但是成員對齊有一個(gè)重要的條件,即每個(gè)成員按照自己的對齊方式對齊; 也就是說(shuō)雖然指定了按n字節對齊,但并不是所有的成員都以n字節對齊。對齊的規則是:每個(gè)成員按其類(lèi)型的對齊參數(通常是這個(gè)類(lèi)型的大小)和指定對齊參數(這里是n字節)中較小的一個(gè)對齊,即min(n,sizeof(item)),并且結構的長(cháng)度必須為所用過(guò)的所有對齊參數的整數倍,不夠就補空字節。

  以瑞薩SH7145F CPU和XASS-V編譯器為例,根據XASS-V幫助文件,對于SH7145F,其結構體成員的默認對齊系數如下:

  char:1

  short:2

  int:4

  long:4

  float:4

  long long:4

  double:4

  指針:4

  結構體/聯(lián)合體:4

  數組:根據類(lèi)型而定

  在編譯的時(shí)候可以通過(guò)設置 /bond = n, n=4(一般情況),來(lái)指定邊界調整系數。因此,實(shí)際采用/bond=n中的n和上述默認對齊系數中的較小者。

  由于跟特定編譯器有關(guān),所以下面的例子僅限XASS-V編譯器,目標CPU是瑞薩SH7145F。

  例1、假設 /bond=4,進(jìn)行如下定義:

  typedef unsigned char UCHAR;

  /* 結構體定義*/

  typedef struct{

  union{

  UCHAR SO_bit7;

  UCHAR DMY0_bit56;

  UCHAR LNO_bit04;

  } DL; /* 預想BYTE0 */

  UCHAR ORDN;

  union{

  UCHAR RW_bit7;

  UCHAR DTNO_bit06;

  }RD; /* 預想BYTE1 */

  UCHAR ADRH; /* 預想BYTE2 */

  UCHAR ADRL; /* 預想BYTE3 */

  UCHAR DATA; /* 預想BYTE4 */

  }stTEST;

  /* 變量定義*/

  UCHAR TEST[6];

  stTEST *pTEM=( stTEST *) TEST;

  這樣執行后,pTEM->ADRH并不是對應TEST [3],導致了數據處理錯誤。

  原因分析:

  由邊界調整數決定,union只能在4的倍數的地址上存放;且UNION類(lèi)型要占用4*X Byte,故后面有3Byte的Dummy。即UNION{xxx}DL占用了4Byte。

  依次類(lèi)推,整個(gè)結構體元素的內存分布如下:

  UNION{xxx}DL; /* Byte0~Byte3: 后3Byte Dummy*/

  UCHAR ORDN; /* Byte4~Byte7: 后3Byte Dummy*/

  UNION{xxx}RD; /* Byte8~Byte11: 后3Byte Dummy*/

  UCHAR ADRH; /* Byte12*/

  UCHAR ADRL; /* Byte13*/

  UCHAR DATA; /* Byte14~Byte15: 后1Byte Dummy*/

  Byte5~Byte7的Dummy是因為UNION成員必須在4的倍數的地址上存放。

  Byte15的Dummy是整個(gè)結構體大小必須是4的倍數。

  所以sizeof(stTEST)=16, pTEM->ADRH對應為Byte12,不是預想的TEST[3]。

  三、由變量類(lèi)型不匹配引起的問(wèn)題

  例1、循環(huán)變量溢出

  UCHAR i = 0x00;

  /* 版本1:100個(gè)循環(huán) */

  for(i=0;i<100;i++) { /* 處理:略*/}

  /* 版本2:1000個(gè)循環(huán) */

  for(i=0;i<1000;i++) { /* 處理:略*/}

  一開(kāi)始需求是100個(gè)循環(huán),而后面需求變更為1000個(gè)循環(huán),但忘記修改循環(huán)變量類(lèi)型。以為UCHAR的有效范圍是0~255,顯然不滿(mǎn)足版本2的要求。這種情況會(huì )發(fā)生在循環(huán)變量定義的位置距離循環(huán)體比較遠的時(shí)候,在無(wú)意識中忽略了。

  四、由數組下標越界引起的問(wèn)題

  常見(jiàn)的是指定的數組下標超過(guò)了數組最大有效下標。很多情況下不會(huì )導致程序奔潰,但是取出的數據顯然是不正確的。

  五、由字節序引起的問(wèn)題

  字節序主要體現大于1Byte的數據的存儲方式上。對于CPU而言,有MSB FIRST和LSB FIRST兩種存儲方式。MSB指Most Significant Bit,即最高有效位;LSB指Lest Significant Bit,即最低有效位。簡(jiǎn)單地說(shuō),MSB FIRST就是高位優(yōu)先存儲,即高位存儲在低地址上,低位存儲在高地址上,簡(jiǎn)稱(chēng)“高低低高”。LSB FIRST則相反,即低位優(yōu)先存儲,高位存儲在高地址上,低位存儲在低地址上,簡(jiǎn)稱(chēng)“高高低低”。大部分嵌入式系統的CPU是MSB FIRST的,少部分是LSB FIRST的。常見(jiàn)的LSB FIRST的CPU是INTEL的。

  類(lèi)似的,在網(wǎng)絡(luò )通信方面有兩種字節序:“Big-Endian”和“Small-Endian”。 指的都是對于多字節的數據類(lèi)型(比如4字節的32位整數),其多個(gè)字節的順序問(wèn)題,是最高字節在前(Big-Endian)還是最低字節在前(Small-Endian)。 比如對于123456789這個(gè)整數,其16進(jìn)制為0x075BCD15,那么按照Big-Endian的方式,它在網(wǎng)絡(luò )上傳輸(或者在內存里存儲)的4個(gè)字節依次是:07 5B CD 15,而Small-Endian的順序正相反,是:15 CD 5B 07。處于通信的雙方必須按相同的字節序進(jìn)行收發(fā)數據處理,才能得出正常的結果。

  例1、應用程序A以075BCD15的字節序(Big-Endian)發(fā)送數據123456789給應用程序B,但應用程序B卻以15CD5B07的字節序(Small-Endian)處理,則雙方?jīng)]法正常通信。

  六、由UNION元素賦值引起的問(wèn)題

  一個(gè)UNION元素的值由最后那次設定決定的。有些時(shí)候,無(wú)意中對一個(gè)UNION元素連續賦值,就會(huì )發(fā)生意料之外的問(wèn)題。

  例1、以前面的結構體類(lèi)型stTEST 為例,做如下設置。

  stTEST tTst;

  tTst.DL.SO_bit7=0x80; /* SO使用bit7 */

  tTst.DL. LNO_bit04=0x01; /* LNO使用bit0~bit4 */

  這樣設置后,最終SO=0,而不是預先希望的SO=1。

  七、由運算符優(yōu)先級引起的問(wèn)題

  運算符優(yōu)先級處理不好也會(huì )引入一些潛在的問(wèn)題。

  例1、邏輯運算符與條件運算符

  int a=0x02;

  if((a&0x03)!=0x00) /*表達式1*/

  if(a&0x03!=0x00) /*表達式2*/

  表達式1:先執行0x02&0x03=>0x02,再執行0x02 !=0x00,故結果為T(mén)RUE。

  表達式2:先執行0x03 !=0x00=>TRUE(結果是0x01),再執行0x02&0x01,結果為0x00即FALSE。

  顯然,表達式1才是正確的寫(xiě)法。如果把用表達式2的形式就會(huì )引入一些潛在的問(wèn)題。

  例2、指針取值運算,邏輯運算符與條件運算符

  if((*pSTSRG&0x01)==0x00) /*表達式1*/

  if(((*pSTSRG)&0x01)==0x00) /*表達式2*/

  if(*pSTSRG&0x01==0x00) /*表達式3*/

  顯然,表達式1和表達式2是正確寫(xiě)法,而且最保險的寫(xiě)法是表達式2。而表達式3是錯誤寫(xiě)法。

  從上面的例子可以看出,由于運算符優(yōu)先級不太方便記憶,也沒(méi)必要去記憶,最規避這類(lèi)問(wèn)題的最好辦法就是給表達式強制加上括號。

  八、由中斷優(yōu)先級引起的問(wèn)題

  在多線(xiàn)程應用程序開(kāi)發(fā)中,經(jīng)常會(huì )用信號量等方式來(lái)保證共享資源的訪(fǎng)問(wèn)。但是在有多個(gè)中斷的應用程序中,經(jīng)常會(huì )無(wú)意識地略掉中斷優(yōu)先級,導致引入一些潛在的問(wèn)題。

  例1、中斷IRQ1每1ms發(fā)生1次,中斷IRQ4每4ms發(fā)生1次,且IRQ4的優(yōu)先級高于IRQ1。在IRQ1和IRQ4的處理過(guò)程都會(huì )設置全局變量tData。該如何安排IRQ1和IRQ4的處理邏輯?

  因為低優(yōu)先級的中斷IRQ1未處理完成時(shí),如果發(fā)生高優(yōu)先級的中斷IRQ4,則IRQ1的處理會(huì )把相關(guān)上下文壓棧,暫時(shí)掛起,等IRQ4處理完成后才繼續處理IRQ1。由于IRQ4也會(huì )修改全局變量tData,如果沒(méi)有任何保護措施,則IRQ1的處理可能會(huì )不完全正確。作為對策之一就是在IRQ1的處理中加入中斷屏蔽。

  /* IRQ1的中斷處理函數 */

  IRQ1_Handler()

  {

  xxx; /* 屏蔽所有中斷 */

  yyy; /* 相關(guān)處理 */

  zzz; /* 解除中斷屏蔽 */

  }

  當然,上述處理的前提是IRQ4是可屏蔽中斷。

  九、由匯編語(yǔ)言轉C語(yǔ)言引起的問(wèn)題

  由于CPU的更換,原先用匯編語(yǔ)言開(kāi)發(fā)的系統轉換為用C語(yǔ)言開(kāi)發(fā)的情形也是存在的。這種情況也經(jīng)常會(huì )引入一些問(wèn)題。

  例1、CPU字節序不同而引起的問(wèn)題

  關(guān)于字節序參考前面的內容。當用到一個(gè)大于1Byte的變量的時(shí)候,必須了解新舊CPU的字節序,正確操作大于1Byte的變量,才能保證不會(huì )因為高低位倒置而引入問(wèn)題。

  例2、CPU頻率不同而引起的問(wèn)題

  在匯編語(yǔ)言開(kāi)發(fā)的系統中,經(jīng)常會(huì )用一些循環(huán)來(lái)實(shí)現微秒級的延時(shí)。特別在串口通信中,硬件寄存器對時(shí)間非常敏感,如果在轉換成C語(yǔ)言時(shí)沒(méi)有考慮到這點(diǎn),沒(méi)有及時(shí)調整循環(huán)次數,就會(huì )因為CPU頻率變高而導致延時(shí)不足。

【嵌入式開(kāi)發(fā)C語(yǔ)言問(wèn)題詳解】相關(guān)文章:

嵌入式開(kāi)發(fā)中C語(yǔ)言中結構體解讀201603-11

嵌入式系統開(kāi)發(fā)人員C語(yǔ)言測試題03-31

2016年嵌入式面試C語(yǔ)言試題「精選」03-07

嵌入式C語(yǔ)言面試題(附答案)03-17

嵌入式Linux詳解03-29

嵌入式開(kāi)發(fā)要學(xué)什么03-27

嵌入式系統開(kāi)發(fā)特點(diǎn)03-20

嵌入式系統開(kāi)發(fā)流程01-29

LabView開(kāi)發(fā)嵌入式應用的技巧03-17

一级日韩免费大片,亚洲一区二区三区高清,性欧美乱妇高清come,久久婷婷国产麻豆91天堂,亚洲av无码a片在线观看