在线看毛片网站电影-亚洲国产欧美日韩精品一区二区三区,国产欧美乱夫不卡无乱码,国产精品欧美久久久天天影视,精品一区二区三区视频在线观看,亚洲国产精品人成乱码天天看,日韩久久久一区,91精品国产91免费

<abbr id="27omo"></abbr>

<menu id="27omo"><dl id="27omo"></dl></menu>
    • <label id="27omo"><tt id="27omo"></tt></label>

      新聞中心

      EEPW首頁 > 設計應用 > 嵌入式軟件開發(fā)常用的套路與技巧

      嵌入式軟件開發(fā)常用的套路與技巧

      作者: 時間:2023-12-12 來源: 收藏

      01. 調(diào)試相關的宏

      在Linux使用gcc編譯程序的時候,對于調(diào)試的語句還具有一些特殊的語法。

      本文引用地址:http://www.biyoush.com/article/202312/453840.htm

      gcc編譯的過程中,會生成一些宏,可以使用這些宏分別打印當前源文件的信息,主要內(nèi)容是當前的文件、當前運行的函數(shù)和當前的程序行。

      具體宏如下:

      __FILE__  當前程序源文件 (char*)
      __FUNCTION__  當前運行的函數(shù) (char*)
      __LINE__  當前的函數(shù)行 (int)

      這些宏不是程序代碼定義的,而是有編譯器產(chǎn)生的。這些信息都是在編譯器處理文件的時候動態(tài)產(chǎn)生的。

      「測試示例:」

      #include 
      int main(void){
          printf("file: %sn", __FILE__);
          printf("function: %sn", __FUNCTION__);
          printf("line: %dn", __LINE__);
          return 0;
      }

      02. # 字符串化操作符

      在gcc的編譯系統(tǒng)中,可以使用#將當前的內(nèi)容轉(zhuǎn)換成字符串。

      「程序示例:」

      #include 
      #define DPRINT(expr) printf("
      %s = %dn", #expr, expr);
      int main(void){
          int x = 3;
          int y = 5;
          DPRINT(x / y);
          DPRINT(x + y);
          DPRINT(x * y);
          
          return 0;
      }

      「執(zhí)行結(jié)果:」

      deng@itcast:~/tmp$ gcc test.c 
      deng@itcast:~/tmp$ ./a.out  
      x / y = 0x + y = 8x * y = 15

      #expr表示根據(jù)宏中的參數(shù)(即表達式的內(nèi)容),生成一個字符串。該過程同樣是有編譯器產(chǎn)生的,編譯器在編譯源文件的時候,如果遇到了類似的宏,會自動根據(jù)程序中表達式的內(nèi)容,生成一個字符串的宏。

      這種方式的優(yōu)點是可以用統(tǒng)一的方法打印表達式的內(nèi)容,在程序的調(diào)試過程中可以方便直觀的看到轉(zhuǎn)換字符串之后的表達式。

      具體的表達式的內(nèi)容是什么,有編譯器自動寫入程序中,這樣使用相同的宏打印所有表達式的字符串。

      //打印字符
      #define debugc(expr) printf(" %s = %cn", #expr, expr)
      //打印浮點數(shù)
      #define debugf(expr) printf(" %s = %fn", #expr, expr)
      //按照16進制打印整數(shù)
      #define debugx(expr) printf(" %s = 0X%xn", #expr, expr);

      由于#expr本質(zhì)上市一個表示字符串的宏,因此在程序中也可以不適用%s打印它的內(nèi)容,而是可以將其直接與其它的字符串連接。

      因此,上述宏可以等價以下形式:

      //打印字符
      #define debugc(expr) printf(" #expr = %cn", expr)
      //打印浮點數(shù)
      #define debugf(expr) printf(" #expr = %fn", expr)
      //按照16進制打印整數(shù)
      #define debugx(expr) printf(" #expr = 0X%xn", expr);

      「總結(jié):」

      #是C語言預處理階段的字符串化操作符,可將宏中的內(nèi)容轉(zhuǎn)換成字符串。

      03. ## 連接操作符

      在gcc的編譯系統(tǒng)中,##是C語言中的連接操作符,可以在編譯的預處理階段實現(xiàn)字符串連接的操作。

      「程序示例:」

      #include 
      #define test(x) test##x
      void test1(int a){
          printf("test1 a = %dn", a);
      }
      void test2(char *s){
          printf("test2 s = %sn", s);
      }
      int main(void){
          test(1)(100);
          test(2)("hello world");
          
          return 0;
      }

      上述程序中,test(x)宏被定義為test##x, 他表示test字符串和x字符串的連接。

      在程序的調(diào)試語句中,##常用的方式如下

      #define DEBUG(fmt, args...) printf(fmt, ##args)

      替換的方式是將參數(shù)的兩個部分以##連接。##表示連接變量代表前面的參數(shù)列表。使用這種形式可以將宏的參數(shù)傳遞給一個參數(shù)。args…是宏的參數(shù),表示可變的參數(shù)列表,使用##args將其傳給printf函數(shù).

      「總結(jié):」

      ##是C語言預處理階段的連接操作符,可實現(xiàn)宏參數(shù)的連接。

      04. 調(diào)試宏第一種形式

      一種定義的方式:

      #define DEBUG(fmt, args...)             
          {                                   
          printf("file:%s function: %s line: %d ", __FILE__, __FUNCTION__, __LINE__);
          printf(fmt, ##args);                
          }

      「程序示例:」

      #include 
      #define DEBUG(fmt, args...)             
          {                                   
          printf("file:%s function: %s line: %d ", __FILE__, __FUNCTION__, __LINE__);
          printf(fmt, ##args);                
          }

      int main(void){
          int a = 100;
          int b = 200;
          char *s = "hello world";
          DEBUG("a = %d b = %dn", a, b);
          DEBUG("a = %x b = %xn", a, b);
          DEBUG("s = %sn", s);
          
          return 0;
      }

      「總結(jié):」

      上面的DEBUG定義的方式是兩條語句的組合,不可能在產(chǎn)生返回值,因此不能使用它的返回值。

      05. 調(diào)試宏的第二種定義方式

      調(diào)試宏的第二種定義方式

      #define DEBUG(fmt, args...)             
          printf("file:%s function: %s line: %d "fmt, 
          __FILE__, __FUNCTION__, __LINE__, ##args)

      程序示例

      #include 
      #define DEBUG(fmt, args...)             
          printf("file:%s function: %s line: %d "fmt, 
          __FILE__, __FUNCTION__, __LINE__, ##args)

      int main(void){
          int a = 100;
          int b = 200;
          char *s = "hello world";
          DEBUG("a = %d b = %dn", a, b);
          DEBUG("a = %x b = %xn", a, b);
          DEBUG("s = %sn", s);
          
          return 0;
      }

      「總結(jié):」

      fmt必須是一個字符串,不能使用指針,只有這樣才可以實現(xiàn)字符串的功能。

      06. 對調(diào)試語句進行分級審查

      即使定義了調(diào)試的宏,在工程足夠大的情況下,也會導致在打開宏開關的時候在終端出現(xiàn)大量的信息。而無法區(qū)分哪些是有用的。

      這個時候就要加入分級檢查機制,可以定義不同的調(diào)試級別,這樣就可以對不同重要程序和不同的模塊進行區(qū)分,需要調(diào)試哪一個模塊就可以打開那一個模塊的調(diào)試級別。

      一般可以利用配置文件的方式顯示,其實Linux內(nèi)核也是這么做的,它把調(diào)試的等級分成了7個不同重要程度的級別,只有設定某個級別可以顯示,對應的調(diào)試信息才會打印到終端上。

      可以寫出一下配置文件

      [debug]
      debug_level=XXX_MODULE

      解析配置文件使用標準的字符串操作庫函數(shù)就可以獲取XXX_MODULE這個數(shù)值。

      int show_debug(int level){
          if (level == XXX_MODULE)
          {
              #define DEBUG(fmt, args...)             
              printf("file:%s function: %s line: %d "fmt, 
              __FILE__, __FUNCTION__, __LINE__, ##args)       

          }
          else if (...)
          {
              ....
          }
      }

      07. 條件編譯調(diào)試語句

      在實際的開發(fā)中,一般會維護兩種源程序,一種是帶有調(diào)試語句的調(diào)試版本程序,另外一種是不帶有調(diào)試語句的發(fā)布版本程序。

      然后根據(jù)不同的條件編譯選項,編譯出不同的調(diào)試版本和發(fā)布版本的程序。

      在實現(xiàn)過程中,可以使用一個調(diào)試宏來控制調(diào)試語句的開關。

      #ifdef USE_DEBUG
              #define DEBUG(fmt, args...)             
              printf("file:%s function: %s line: %d "fmt, 
              __FILE__, __FUNCTION__, __LINE__, ##args)  

      #else
        #define DEBUG(fmt, args...)
      #endif

      如果USE_DEBUG被定義,那么有調(diào)試信息,否則DEBUG就為空。

      如果需要調(diào)試信息,就只需要在程序中更改一行就可以了。

      #define USE_DEBUG
      #undef USE_DEBUG

      定義條件編譯的方式使用一個帶有值的宏

      #if USE_DEBUG
              #define DEBUG(fmt, args...)             
              printf("file:%s function: %s line: %d "fmt, 
              __FILE__, __FUNCTION__, __LINE__, ##args)  

      #else
        #define DEBUG(fmt, args...)
      #endif

      可以使用如下方式進行條件編譯

      #ifndef USE_DEBUG
      #define USE_DEBUG 0
      #endif

      08. 使用do…while的宏定義

      使用宏定義可以將一些較為短小的功能封裝,方便使用。宏的形式和函數(shù)類似,但是可以節(jié)省函數(shù)跳轉(zhuǎn)的開銷。

      如何將一個語句封裝成一個宏,在程序中常常使用do…while(0)的形式。

      #define HELLO(str) do { 
      printf("hello: %sn", str); 
      }while(0)

      「程序示例:」

      int cond = 1;
      if (cond)
          HELLO("true");
      else
          HELLO("false");

      09. 代碼剖析

      對于比較大的程序,可以借助一些工具來首先把需要優(yōu)化的點清理出來。接下來我們來看看在程序執(zhí)行過程中獲取數(shù)據(jù)并進行分析的工具:代碼剖析程序。

      「測試程序:」

      #include 
      #define T 100000
      void call_one(){
          int count = T * 1000;
          while(count--);
      }
      void call_two(){
          int count = T * 50;
          while(count--);
      }
      void call_three(){
          int count = T * 20;
          while(count--);
      }
      int main(void){
          int time = 10;
          while(time--)
          {
              call_one();
              call_two();
              call_three();
          }
          
          return 0;
      }

      編譯的時候加入-pg選項:

      deng@itcast:~/tmp$ gcc -pg  test.c -o test

      執(zhí)行完成后,在當前文件中生成了一個gmon.out文件。

      deng@itcast:~/tmp$ ./test  
      deng@itcast:~/tmp$ ls
      gmon.out  test  test.c
      deng@itcast:~/tmp$ 

      「使用gprof剖析主程序:」

      deng@itcast:~/tmp$ gprof test
      Flat profile:
      Each sample counts as 0.01 seconds.
        %   cumulative   self              self     total           
       time   seconds   seconds    calls  ms/call  ms/call  name    
       95.64      1.61     1.61       10   160.68   160.68  call_one
        3.63      1.67     0.06       10     6.10     6.10  call_two
        2.42      1.71     0.04       10     4.07     4.07  call_three

      其中主要的信息有兩個,一個是每個函數(shù)執(zhí)行的時間占程序總時間的百分比,另外一個就是函數(shù)被調(diào)用的次數(shù)。通過這些信息,可以優(yōu)化核心程序的實現(xiàn)方式來提高效率。

      當然這個剖析程序由于它自身特性有一些限制,比較適用于運行時間比較長的程序,因為統(tǒng)計的時間是基于間隔計數(shù)這種機制,所以還需要考慮函數(shù)執(zhí)行的相對時間,如果程序執(zhí)行時間過短,那得到的信息是沒有任何參考意義的。

      「將上訴程序時間縮短:」

      #include 
      #define T 100
      void call_one(){
          int count = T * 1000;
          while(count--);
      }
      void call_two(){
          int count = T * 50;
          while(count--);
      }
      void call_three(){
          int count = T * 20;
          while(count--);
      }
      int main(void){
          int time = 10;
          while(time--)
          {
              call_one();
              call_two();
              call_three();
          }
          
          return 0;
      }

      「剖析結(jié)果如下:」

      deng@itcast:~/tmp$ gcc -pg test.c -o test
      deng@itcast:~/tmp$ ./test  
      deng@itcast:~/tmp$ gprof test
      Flat profile:
      Each sample counts as 0.01 seconds.
       no time accumulated
        %   cumulative   self              self     total           
       time   seconds   seconds    calls  Ts/call  Ts/call  name    
        0.00      0.00     0.00       10     0.00     0.00  call_one
        0.00      0.00     0.00       10     0.00     0.00  call_three
        0.00      0.00     0.00       10     0.00     0.00  call_two

      因此該剖析程序?qū)τ谠綇碗s、執(zhí)行時間越長的函數(shù)也適用。

      那么是不是每個函數(shù)執(zhí)行的絕對時間越長,剖析顯示的時間就真的越長呢?可以再看如下的例子

      #include 
      #define T 100
      void call_one(){
          int count = T * 1000;
          while(count--);
      }
      void call_two(){
          int count = T * 100000;
          while(count--);
      }
      void call_three(){
          int count = T * 20;
          while(count--);
      }
      int main(void){
          int time = 10;
          while(time--)
          {
              call_one();
              call_two();
              call_three();
          }
          
          return 0;
      }

      「剖析結(jié)果如下:」

      deng@itcast:~/tmp$ gcc -pg test.c -o test
      deng@itcast:~/tmp$ ./test  
      deng@itcast:~/tmp$ gprof test
      Flat profile:
      Each sample counts as 0.01 seconds.
        %   cumulative   self              self     total           
       time   seconds   seconds    calls  ms/call  ms/call  name    
      101.69      0.15     0.15       10    15.25    15.25  call_two
        0.00      0.15     0.00       10     0.00     0.00  call_one
        0.00      0.15     0.00       10     0.00     0.00  call_three

      「總結(jié):」

      在使用gprof工具的時候,對于一個函數(shù)進行gprof方式的剖析,實質(zhì)上的時間是指除去庫函數(shù)調(diào)用和系統(tǒng)調(diào)用之外,純碎應用部分開發(fā)的實際代碼運行的時間,也就是說time一項描述的時間值不包括庫函數(shù)printf、系統(tǒng)調(diào)用system等運行的時間。

      這些實用庫函數(shù)的程序雖然運行的時候?qū)⒈茸畛醯某绦驅(qū)嵱酶嗟臅r間,但是對于剖析函數(shù)來說并沒有影響。



      評論


      相關推薦

      技術專區(qū)

      關閉