<dfn id="w48us"></dfn><ul id="w48us"></ul>
  • <ul id="w48us"></ul>
  • <del id="w48us"></del>
    <ul id="w48us"></ul>
  • 嵌入式開發(fā)C語言問題詳解

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

    2016年嵌入式開發(fā)C語言問題詳解

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

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

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

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

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

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

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

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

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

      unsigned char *pTSTR;

      pTSTR = TSTREG;

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

      *pTSTR = 0x02;

      ……

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

      volatile unsigned char *pTSTR;

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

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

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

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

      二、由字節(jié)對齊引起的問題

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

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

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

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

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

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

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

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

      以瑞薩SH7145F CPU和XASS-V編譯器為例,根據(jù)XASS-V幫助文件,對于SH7145F,其結(jié)構(gòu)體成員的默認(rèn)對齊系數(shù)如下:

      char:1

      short:2

      int:4

      long:4

      float:4

      long long:4

      double:4

      指針:4

      結(jié)構(gòu)體/聯(lián)合體:4

      數(shù)組:根據(jù)類型而定

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

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

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

      typedef unsigned char UCHAR;

      /* 結(jié)構(gòu)體定義*/

      typedef struct{

      union{

      UCHAR SO_bit7;

      UCHAR DMY0_bit56;

      UCHAR LNO_bit04;

      } DL; /* 預(yù)想BYTE0 */

      UCHAR ORDN;

      union{

      UCHAR RW_bit7;

      UCHAR DTNO_bit06;

      }RD; /* 預(yù)想BYTE1 */

      UCHAR ADRH; /* 預(yù)想BYTE2 */

      UCHAR ADRL; /* 預(yù)想BYTE3 */

      UCHAR DATA; /* 預(yù)想BYTE4 */

      }stTEST;

      /* 變量定義*/

      UCHAR TEST[6];

      stTEST *pTEM=( stTEST *) TEST;

      這樣執(zhí)行后,pTEM->ADRH并不是對應(yīng)TEST [3],導(dǎo)致了數(shù)據(jù)處理錯誤。

      原因分析:

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

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

      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的倍數(shù)的地址上存放。

      Byte15的Dummy是整個結(jié)構(gòu)體大小必須是4的倍數(shù)。

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

      三、由變量類型不匹配引起的問題

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

      UCHAR i = 0x00;

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

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

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

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

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

      四、由數(shù)組下標(biāo)越界引起的問題

      常見的是指定的數(shù)組下標(biāo)超過了數(shù)組最大有效下標(biāo)。很多情況下不會導(dǎo)致程序奔潰,但是取出的數(shù)據(jù)顯然是不正確的。

      五、由字節(jié)序引起的問題

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

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

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

      六、由UNION元素賦值引起的問題

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

      例1、以前面的結(jié)構(gòu)體類型stTEST 為例,做如下設(shè)置。

      stTEST tTst;

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

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

      這樣設(shè)置后,最終SO=0,而不是預(yù)先希望的SO=1。

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

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

      例1、邏輯運(yùn)算符與條件運(yùn)算符

      int a=0x02;

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

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

      表達(dá)式1:先執(zhí)行0x02&0x03=>0x02,再執(zhí)行0x02 !=0x00,故結(jié)果為TRUE。

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

      顯然,表達(dá)式1才是正確的寫法。如果把用表達(dá)式2的形式就會引入一些潛在的問題。

      例2、指針取值運(yùn)算,邏輯運(yùn)算符與條件運(yùn)算符

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

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

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

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

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

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

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

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

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

      /* IRQ1的中斷處理函數(shù) */

      IRQ1_Handler()

      {

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

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

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

      }

      當(dāng)然,上述處理的前提是IRQ4是可屏蔽中斷。

      九、由匯編語言轉(zhuǎn)C語言引起的問題

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

      例1、CPU字節(jié)序不同而引起的問題

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

      例2、CPU頻率不同而引起的問題

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

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

    嵌入式開發(fā)中C語言中結(jié)構(gòu)體解讀201603-11

    嵌入式系統(tǒng)開發(fā)人員C語言測試題03-31

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

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

    嵌入式Linux詳解03-29

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

    嵌入式系統(tǒng)開發(fā)特點(diǎn)03-20

    嵌入式系統(tǒng)開發(fā)流程01-29

    LabView開發(fā)嵌入式應(yīng)用的技巧03-17

    主站蜘蛛池模板: 午夜一级日韩精品制服诱惑我们这边| 久久久久99精品成人片欧美| 色妞ww精品视频7777| 久久se精品一区二区影院 | 99久久er这里只有精品18| 久久成人国产精品| 国产精品免费看久久久香蕉| 国语自产精品视频在线观看| 久久精品二区| 亚洲精品第一国产综合精品99| 亚洲AV无码国产精品色午友在线| 国产成人精品福利网站在线观看| 精品国产日韩亚洲一区| 99国产精品久久| 国产精品偷伦视频免费观看了| 久久久久国产精品麻豆AR影院| 国产精品岛国久久久久| 大胸国产精品视频| 国产精品熟女高潮视频| 精品国产日产一区二区三区| 国产999精品久久久久久| 91精品欧美综合在线观看| 亚洲精品无码高潮喷水在线| 国产精品无码无需播放器| 亚洲爆乳精品无码一区二区| 国产在线精品一区二区高清不卡| 久久亚洲国产欧洲精品一| 精品视频一区二区三区| 777被窝午夜精品影院| 午夜精品久久久久久久无码| 国产精品免费在线播放| 日韩精品免费在线视频| 中文无码精品一区二区三区| 国产精品岛国久久久久| 国产精品无码无在线观看| 欧美精品v欧洲精品| 午夜精品视频在线| 国产精品欧美一区二区三区不卡| 国产精品日本一区二区在线播放| 久久国产精品成人片免费| 精品久久久久中文字幕日本|