




版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
Word總結嵌入式C語言程序調試和宏使用技巧1.調試相關的宏
在(Linux)使用gcc編譯程序的時候,對于調試的語句還具有一些特殊的語法。gcc編譯的過程中,會生成一些宏,可以使用這些宏分別打印當前源文件的信息,主要內容是當前的文件、當前運行的函數(shù)和當前的程序行。
具體宏如下:
__FILE__當前程序源文件(char*)__FUNC(TI)ON__當前運行的函數(shù)(char*)__LINE__當前的函數(shù)行(int)
這些宏不是(程序代碼)定義的,而是有編譯器產生的。這些信息都是在編譯器處理文件的時候動態(tài)產生的。
測試示例:#includeintmain(void){printf("file:%s",__FILE__);printf("function:%s",__FUNCTION__);printf("line:%d",__LINE__);return0;}2.#字符串化操作符
在gcc的編譯系統(tǒng)中,可以使用#將當前的內容轉換成字符串。
程序示例:#include#defineDPRINT(expr)printf("%s=%d",#expr,expr);intmain(void){intx=3;inty=5;DPRINT(x/y);DPRINT(x+y);DPRINT(x*y);return0;}
執(zhí)行結果:deng@itcast:~/tmp$gcc(te)st.cdeng@itcast:~/tmp$./a.outx/y=0x+y=8x*y=15
#expr表示根據(jù)宏中的參數(shù)(即表達式的內容),生成一個字符串。該過程同樣是有編譯器產生的,編譯器在編譯源文件的時候,如果遇到了類似的宏,會自動根據(jù)程序中表達式的內容,生成一個字符串的宏。
這種方式的優(yōu)點是可以用統(tǒng)一的方法打印表達式的內容,在程序的調試過程中可以方便直觀的看到轉換字符串之后的表達式。具體的表達式的內容是什么,有編譯器自動寫入程序中,這樣使用相同的宏打印所有表達式的字符串。//打印字符#definedebugc(expr)printf("%s=%c",#expr,expr)//打印浮點數(shù)#definedebugf(expr)printf("%s=%f",#expr,expr)//按照16進制打印整數(shù)#definedebugx(expr)printf("%s=0X%x",#expr,expr);
由于#expr本質上市一個表示字符串的宏,因此在程序中也可以不適用%s打印它的內容,而是可以將其直接與其它的字符串連接。因此,上述宏可以等價以下形式://打印字符#definedebugc(expr)printf("#expr=%c",expr)//打印浮點數(shù)#definedebugf(expr)printf("#expr=%f",expr)//按照16進制打印整數(shù)#definedebugx(expr)printf("#expr=0X%x",expr);
總結
#是(C語言)預處理階段的字符串化操作符,可將宏中的內容轉換成字符串。
3.##連接操作符
在gcc的編譯系統(tǒng)中,##是C語言中的連接操作符,可以在編譯的預處理階段實現(xiàn)字符串連接的操作。
程序示例:#include#definetest(x)test##xvoidtest1(inta){printf("test1a=%d",a);}voidtest2(char*s){printf("test2s=%s",s);}intmain(void){test(1)(100);test(2)("helloworld");return0;}
上述程序中,test(x)宏被定義為test##x,他表示test字符串和x字符串的連接。
在程序的調試語句中,##常用的方式如下:#defineDEBUG(fmt,args...)printf(fmt,##args)
替換的方式是將參數(shù)的兩個部分以##連接。##表示連接變量代表前面的參數(shù)列表。使用這種形式可以將宏的參數(shù)傳遞給一個參數(shù)。args…是宏的參數(shù),表示可變的參數(shù)列表,使用##args將其傳給printf函數(shù)。
總結
##是C語言預處理階段的連接操作符,可實現(xiàn)宏參數(shù)的連接。
4.調試宏第一種形式
一種定義的方式:#defineDEBUG(fmt,args...){printf("file:%sfunction:%sline:%d",__FILE__,__FUNCTION__,__LINE__);printf(fmt,##args);}
程序示例:#include#defineDEBUG(fmt,args...){printf("file:%sfunction:%sline:%d",__FILE__,__FUNCTION__,__LINE__);printf(fmt,##args);}intmain(void){inta=100;intb=200;char*s="helloworld";DEBUG("a=%db=%d",a,b);DEBUG("a=%xb=%x",a,b);DEBUG("s=%s",s);return0;}
總結
上面的DEBUG定義的方式是兩條語句的組合,不可能在產生返回值,因此不能使用它的返回值。
5.調試宏的第二種定義方式
調試宏的第二種定義方式。#defineDEBUG(fmt,args...)printf("file:%sfunction:%sline:%d"fmt,__FILE__,__FUNCTION__,__LINE__,##args)
程序示例:#include#defineDEBUG(fmt,args...)printf("file:%sfunction:%sline:%d"fmt,__FILE__,__FUNCTION__,__LINE__,##args)intmain(void){inta=100;intb=200;char*s="helloworld";DEBUG("a=%db=%d",a,b);DEBUG("a=%xb=%x",a,b);DEBUG("s=%s",s);return0;}
總結
fmt必須是一個字符串,不能使用指針,只有這樣才可以實現(xiàn)字符串的功能。
6.對調試語句進行分級審查
即使定義了調試的宏,在工程足夠大的情況下,也會導致在打開宏開關的時候在終端出現(xiàn)大量的信息。而無法區(qū)分哪些是有用的。這個時候就要加入分級檢查機制,可以定義不同的調試級別,這樣就可以對不同重要程序和不同的模塊進行區(qū)分,需要調試哪一個模塊就可以打開那一個模塊的調試級別。
一般可以利用配置文件的方式顯示,其實Linux內核也是這么做的,它把調試的等級分成了7個不同重要程度的級別,只有設定某個級別可以顯示,對應的調試信息才會打印到終端上。
可以寫出一下配置文件。[debug]debug_level=XXX_MODULE
解析配置文件使用標準的字符串操作庫函數(shù)就可以獲取XXX_MODULE這個數(shù)值。intshow_debug(intlevel){if(level==XXX_MODULE){#defineDEBUG(fmt,args...)printf("file:%sfunction:%sline:%d"fmt,__FILE__,__FUNCTION__,__LINE__,##args)}elseif(...){}}
7.條件編譯調試語句
在實際的開發(fā)中,一般會維護兩種源程序,一種是帶有調試語句的調試版本程序,另外一種是不帶有調試語句的發(fā)布版本程序。然后根據(jù)不同的條件編譯選項,編譯出不同的調試版本和發(fā)布版本的程序。
在實現(xiàn)過程中,可以使用一個調試宏來控制調試語句的開關。#ifdefUSE_DEBUG#defineDEBUG(fmt,args...)printf("file:%sfunction:%sline:%d"fmt,__FILE__,__FUNCTION__,__LINE__,##args)#else#defineDEBUG(fmt,args...)#endif
如果USE_DEBUG被定義,那么有調試信息,否則DEBUG就為空。
如果需要調試信息,就只需要在程序中更改一行就可以了。#defineUSE_DEBUG#undefUSE_DEBUG
定義條件編譯的方式使用一個帶有值的宏。#ifUSE_DEBUG#defineDEBUG(fmt,args...)printf("file:%sfunction:%sline:%d"fmt,__FILE__,__FUNCTION__,__LINE__,##args)#else#defineDEBUG(fmt,args...)#endif
可以使用如下方式進行條件編譯。#ifndefUSE_DEBUG#defineUSE_DEBUG0#endif
8.使用do…while的宏定義
使用宏定義可以將一些較為短小的功能封裝,方便使用。宏的形式和函數(shù)類似,但是可以節(jié)省函數(shù)跳轉的開銷。如何將一個語句封裝成一個宏,在程序中常常使用do…while(0)的形式。#defineHELLO(str)do{printf("hello:%s",str);}while(0)
程序示例:intcond=1;if(cond)HELLO("true");elseHELLO("false");
9.代碼剖析
對于比較大的程序,可以借助一些工具來首先把需要優(yōu)化的點清理出來。接下來我們來看看在程序執(zhí)行過程中獲取數(shù)據(jù)并進行分析的工具:代碼剖析程序。
測試程序:#include#defineT100000voidcall_one(){intcount=T*1000;while(count--);}voidcall_two(){intcount=T*50;while(count--);}voidcall_three(){intcount=T*20;while(count--);}intmain(void){inttime=10;while(time--){call_one();call_two();call_three();}return0;}
編譯的時候加入-pg選項。deng@itcast:~/tmp$gcc-pgtest.c-otest
執(zhí)行完成后,在當前文件中生成了一個gmon.out文件。deng@itcast:~/tmp$./testdeng@itcast:~/tmp$lsgmon.outtesttest.cdeng@itcast:~/tmp$
使用gprof剖析主程序。
deng@itcast:~/tmp$gproftestFlatprofile:Eachsamplecountsas0.01seconds.%cumulativeselfselftotaltimesecondssecondscallsms/callms/callname95.641.611.6110160.68160.68call_one3.631.670.06106.106.10call_two2.421.710.04104.074.07call_three
其中主要的信息有兩個,一個是每個函數(shù)執(zhí)行的時間占程序總時間的百分比,另外一個就是函數(shù)被調用的次數(shù)。通過這些信息,可以優(yōu)化核心程序的實現(xiàn)方式來提高效率。
當然這個剖析程序由于它自身特性有一些限制,比較適用于運行時間比較長的程序,因為統(tǒng)計的時間是基于間隔計數(shù)這種機制,所以還需要考慮函數(shù)執(zhí)行的相對時間,如果程序執(zhí)行時間過短,那得到的信息是沒有任何參考意義的。
將上述程序時間縮短。
#include#defineT100voidcall_one(){intcount=T*1000;while(count--);}voidcall_two(){intcount=T*50;while(count--);}voidcall_three(){intcount=T*20;while(count--);}intmain(void){inttime=10;while(time--){call_one();call_two();call_three();}return0;}
剖析結果如下。
deng@itcast:~/tmp$gcc-pgtest.c-otestdeng@itcast:~/tmp$./testdeng@itcast:~/tmp$gproftestFlatprofile:Eachsamplecountsas0.01seconds.notimeaccumulated%cumulativeselfselftotaltimesecondssecondscallsTs/callTs/callname0.000.000.00100.000.00call_one0.000.000.00100.000.00call_three0.000.000.00100.000.00call_two
因此該剖析程序對于越復雜、執(zhí)行時間越長的函數(shù)也適用。
那么是不是每個函數(shù)執(zhí)行的絕對時間越長,剖析顯示的時間就真的越長呢?可以再看如下的例子。#include#defineT100voidcall_one(){intcount=T*1000;while(count--);}voidcall_two(){intcount=T*100000;while(count--);}voidcall_three(){intcount=T*20;while(count--);}intmain(void)
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 【正版授權】 IEC 60670-24:2024 EXV EN Boxes and enclosures for electrical accessories for household and similar fixed electrical installations - Part 24: Particular requirements for encl
- 2025-2030年中國鎂錳電池市場規(guī)模分析及發(fā)展建議研究報告
- 2025-2030年中國辣椒制品行業(yè)運行動態(tài)與投資戰(zhàn)略研究報告
- 2025-2030年中國蒿甲醚行業(yè)市場現(xiàn)狀調研與前景規(guī)模預測報告
- 2025-2030年中國自動高壓蒸汽滅菌器市場發(fā)展狀況及前景趨勢分析報告
- 2025-2030年中國育發(fā)水市場發(fā)展狀況及投資規(guī)劃研究報告
- 2025安全員-C證考試題庫
- 2025-2030年中國糯玉米汁飲料市場發(fā)展預測及前景調研分析報告
- 2025-2030年中國粉針類頭孢制劑行業(yè)需求分析與十三五規(guī)劃研究報告
- 2025-2030年中國移動電源車產業(yè)運行動態(tài)及前景趨勢預測報告
- 2023年高中音樂課件陽關三疊(琴歌)
- 胸腔閉式引流護理-中華護理學會團體標準
- 公司資產情況說明范文百度
- 醫(yī)療器械中有害殘留物檢驗技術
- 2015-2022年大慶醫(yī)學高等專科學校高職單招語文/數(shù)學/英語筆試參考題庫含答案解析
- 產品過程特殊特性初始清單(示例)
- 兩篇古典英文版成語故事塞翁失馬
- 中國古代文學史 馬工程課件(中)13第五編 宋代文學 第一章 北宋初期文學
- GB/T 14643.4-2009工業(yè)循環(huán)冷卻水中菌藻的測定方法第4部分:土壤真菌的測定平皿計數(shù)法
- DL-T 5190.1-2022 電力建設施工技術規(guī)范 第1部分:土建結構工程(附條文說明)
- GA/T 914-2010聽力障礙的法醫(yī)學評定
評論
0/150
提交評論