青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

牽著老婆滿街逛

嚴(yán)以律己,寬以待人. 三思而后行.
GMail/GTalk: yanglinbo#google.com;
MSN/Email: tx7do#yahoo.com.cn;
QQ: 3 0 3 3 9 6 9 2 0 .

調(diào)試技巧之調(diào)用堆棧

簡單介紹

調(diào)試是程序開發(fā)者必備技巧。如果不會調(diào)試,自己寫的程序一旦出問題,往往無從下手。本人總結(jié)10年使用VC經(jīng)驗(yàn),對調(diào)試技巧做一個粗淺的介紹。希望對大家有所幫助。

今天簡單的介紹介紹調(diào)用堆棧。調(diào)用堆棧在我的專欄的文章VC調(diào)試入門提了一下,但是沒有詳細(xì)介紹。

首先介紹一下什么叫調(diào)用堆棧:假設(shè)我們有幾個函數(shù),分別是function1,function2,function3,funtion4,且function1調(diào)用function2,function2調(diào)用function3,function3調(diào)用function4。在function4運(yùn)行過程中,我們可以從線程當(dāng)前堆棧中了解到調(diào)用他的那幾個函數(shù)分別是誰。把函數(shù)的順序關(guān)系看,function4、function3、function2、function1呈現(xiàn)出一種“堆棧”的特征,最后被調(diào)用的函數(shù)出現(xiàn)在最上方。因此稱呼這種關(guān)系為調(diào)用堆棧(call stack)。

當(dāng)故障發(fā)生時,如果程序被中斷,我們基本上只可以看到最后出錯的函數(shù)。利用call stack,我們可以知道當(dāng)出錯函數(shù)被誰調(diào)用的時候出錯。這樣一層層的看上去,有時可以猜測出錯誤的原因。常見的這種中斷時ASSERT宏導(dǎo)致的中斷。

在程序被中斷時,debug工具條的右側(cè)倒數(shù)第二個按鈕一般是call stack按鈕,這個按鈕被按下后,你就可以看到當(dāng)前的調(diào)用堆棧。

實(shí)例一:介紹

我們首先演示一下調(diào)用堆棧。首先我們創(chuàng)建一個名為Debug的對話框工程。工程創(chuàng)建好以后,雙擊OK按鈕創(chuàng)建消息映射函數(shù),并添加如下代碼:

void CDebugDlg::OnOK() 
{ 
// TODO: Add extra validation here ASSERT(FALSE);
}

我們按F5開始調(diào)試程序。程序運(yùn)行后,點(diǎn)擊OK按鈕,程序就會被中斷。這時查看call stack窗口,就會發(fā)現(xiàn)內(nèi)容如下:

CDebugDlg::OnOK() line 176 + 34 bytes
_AfxDispatchCmdMsg(CCmdTarget * 0x0012fe74 {CDebugDlg}, unsigned int 1, int 0, void (void)* 0x5f402a00 `vcall'(void), void * 0x00000000, unsigned int 12, AFX_CMDHANDLERINFO * 0x00000000) line 88
CCmdTarget::OnCmdMsg(unsigned int 1, int 0, void * 0x00000000, AFX_CMDHANDLERINFO * 0x00000000) line 302 + 39 bytes
CDialog::OnCmdMsg(unsigned int 1, int 0, void * 0x00000000, AFX_CMDHANDLERINFO * 0x00000000) line 97 + 24 bytes
CWnd::OnCommand(unsigned int 1, long 656988) line 2088
CWnd::OnWndMsg(unsigned int 273, unsigned int 1, long 656988, long * 0x0012f83c) line 1597 + 28 bytes
CWnd::WindowProc(unsigned int 273, unsigned int 1, long 656988) line 1585 + 30 bytes
AfxCallWndProc(CWnd * 0x0012fe74 {CDebugDlg hWnd=???}, HWND__ * 0x001204b0, unsigned int 273, unsigned int 1, long 656988) line 215 + 26 bytes
AfxWndProc(HWND__ * 0x001204b0, unsigned int 273, unsigned int 1, long 656988) line 368
AfxWndProcBase(HWND__ * 0x001204b0, unsigned int 273, unsigned int 1, long 656988) line 220 + 21 bytes
USER32! 77d48709()
USER32! 77d487eb()
USER32! 77d4b368()
USER32! 77d4b3b4()
NTDLL! 7c90eae3()
USER32! 77d4b7ab()
USER32! 77d7fc9d()
USER32! 77d76530()
USER32! 77d58386()
USER32! 77d5887a()
USER32! 77d48709()
USER32! 77d487eb()
USER32! 77d489a5()
USER32! 77d489e8()
USER32! 77d6e819()
USER32! 77d65ce2()
CWnd::IsDialogMessageA(tagMSG * 0x004167d8 {msg=0x00000202 wp=0x00000000 lp=0x000f001c}) line 182
CWnd::PreTranslateInput(tagMSG * 0x004167d8 {msg=0x00000202 wp=0x00000000 lp=0x000f001c}) line 3424
CDialog::PreTranslateMessage(tagMSG * 0x004167d8 {msg=0x00000202 wp=0x00000000 lp=0x000f001c}) line 92
CWnd::WalkPreTranslateTree(HWND__ * 0x001204b0, tagMSG * 0x004167d8 {msg=0x00000202 wp=0x00000000 lp=0x000f001c}) line 2667 + 18 bytes
CWinThread::PreTranslateMessage(tagMSG * 0x004167d8 {msg=0x00000202 wp=0x00000000 lp=0x000f001c}) line 665 + 18 bytes
CWinThread::PumpMessage() line 841 + 30 bytes
CWnd::RunModalLoop(unsigned long 4) line 3478 + 19 bytes
CDialog::DoModal() line 536 + 12 bytes
CDebugApp::InitInstance() line 59 + 8 bytes
AfxWinMain(HINSTANCE__ * 0x00400000, HINSTANCE__ * 0x00000000, char * 0x00141f00, int 1) line 39 + 11 bytes
WinMain(HINSTANCE__ * 0x00400000, HINSTANCE__ * 0x00000000, char * 0x00141f00, int 1) line 30
WinMainCRTStartup() line 330 + 54 bytes
KERNEL32! 7c816d4f()

這里,CDebugDialog::OnOK作為整個調(diào)用鏈中最后被調(diào)用的函數(shù)出現(xiàn)在call stack的最上方,而內(nèi)核中程序的啟動函數(shù)Kernel32! 7c816d4f()則作為棧底出現(xiàn)在最下方。

實(shí)例二:學(xué)習(xí)處理方法

微軟提供了MDI/SDI模型提供文檔處理的建議結(jié)構(gòu)。有些時候,大家希望控制某個環(huán)節(jié)。例如,我們希望彈出自己的打開文件對話框,但是并不想自己實(shí)現(xiàn)整個文檔的打開過程,而更愿意MFC完成其他部分的工作。可是,我們并不清楚MFC是怎么處理文檔的,也不清楚如何插入自定義代碼。

幸運(yùn)的是,我們知道當(dāng)一個文檔被打開以后,系統(tǒng)會調(diào)用CDocument派生類的Serialize函數(shù),我們可以利用這一點(diǎn)來跟蹤MFC的處理過程。

我們首先創(chuàng)建一個缺省的SDI工程Test1,并在CTest1Doc::Serialize函數(shù)的開頭增加一個斷點(diǎn),運(yùn)行程序,并打開一個文件。這時,我們可以看到調(diào)用堆棧是(我只截取了感興趣的一段):

CTest1Doc::Serialize(CArchive & {...}) line 66
CDocument::OnOpenDocument(const char * 0x0012f54c) line 714
CSingleDocTemplate::OpenDocumentFile(const char * 0x0012f54c, int 1) line 168 + 15 bytes
CDocManager::OpenDocumentFile(const char * 0x0042241c) line 953
CWinApp::OpenDocumentFile(const char * 0x0042241c) line 93
CDocManager::OnFileOpen() line 841
CWinApp::OnFileOpen() line 37
_AfxDispatchCmdMsg(CCmdTarget * 0x004177f0 class CTest1App  theApp, unsigned int 57601, int 0, void (void)* 0x00402898 CWinApp::OnFileOpen, void * 0x00000000, unsigned int 12, AFX_CMDHANDLERINFO * 0x00000000) line 88
CCmdTarget::OnCmdMsg(unsigned int 57601, int 0, void * 0x00000000, AFX_CMDHANDLERINFO * 0x00000000) line 302 + 39 bytes
CFrameWnd::OnCmdMsg(unsigned int 57601, int 0, void * 0x00000000, AFX_CMDHANDLERINFO * 0x00000000) line 899 + 33 bytes
CWnd::OnCommand(unsigned int 57601, long 132158) line 2088
CFrameWnd::OnCommand(unsigned int 57601, long 132158) line 317

從上面的調(diào)用堆棧看,這個過程由一個WM_COMMAND消息觸發(fā)(因?yàn)槲覀冇貌藛未蜷_文件),由CWinApp::OnFileOpen最先開始實(shí)際處理過程,這個函數(shù)調(diào)用CDocManager::OnFileOpen打開文檔。

我們首先雙擊CWinApp::OnFileOpen() line 37打開CWinApp::OnFileOpen,它的處理過程是:

    ASSERT(m_pDocManager != NULL);
    m_pDocManager->OnFileOpen();

m_pDocManager是一個CDocManager類的實(shí)例指針,我們雙擊CDocManager::OnFileOpen行,看該函數(shù)的實(shí)現(xiàn):

void CDocManager::OnFileOpen()
{
 // prompt the user (with all document templates)
 CString newName;
 if (!DoPromptFileName(newName, AFX_IDS_OPENFILE,
   OFN_HIDEREADONLY | OFN_FILEMUSTEXIST, TRUE, NULL))
  return; // open cancelled
 AfxGetApp()->OpenDocumentFile(newName);
  // if returns NULL, the user has already been alerted
}

很顯然,該函數(shù)首先調(diào)用DoPromptFileName函數(shù)來獲得一個文件名,然后在繼續(xù)后續(xù)的打開過程。

順這這個線索下去,我們一定能找到插入我們文件打開對話框的位置。由于這不是我們研究的重點(diǎn),后續(xù)的分析我就不再詳述。

實(shí)例三:內(nèi)存訪問越界

在Debug版本的VC程序中,程序會給每塊new出來的內(nèi)存,預(yù)留幾個字節(jié)作為越界檢測之用。在釋放內(nèi)存時,系統(tǒng)會檢查這幾個字節(jié),判斷是否有內(nèi)存訪問越界的可能。

我們借用前一個實(shí)例程序,在CTest1App::InitInstance的開頭添加以下幾行代碼:

    char * p = new char[10];
    memset(p,0,100);
    delete []p;
    return FALSE;

很顯然,這段代碼申請了10字節(jié)內(nèi)存,但是使用了100字節(jié)。我們在memset(p,0,100);這行加一個斷點(diǎn),然后執(zhí)行程序,斷點(diǎn)到達(dá)后,我們觀察p指向的內(nèi)存的值(利用Debug工具條的Memory功能),可以發(fā)現(xiàn)它的值是:

  CD CD CD CD CD CD CD CD
  CD CD FD FD FD FD FD FD
  00 00 00 00 00 00 00 00
  ......

根據(jù)經(jīng)驗(yàn),p實(shí)際被分配了16個字節(jié),后6個字節(jié)用于保護(hù)。我們按F5全速執(zhí)行程序,會發(fā)現(xiàn)如下的錯誤信息被彈出:

   Debug Error!
   Program: c:\temp\test1\Debug\test1.exe
   DAMAGE: after normal block (#55) at 0x00421AB0
   Press Retry to debug the application

該信息提示,在正常內(nèi)存塊0x00421AB0后的內(nèi)存被破壞(內(nèi)存訪問越界),我們點(diǎn)擊Retry進(jìn)入調(diào)試狀態(tài),發(fā)現(xiàn)調(diào)用堆棧是:

_free_dbg_lk(void * 0x00421ab0, int 1) line 1033 + 60 bytes
_free_dbg(void * 0x00421ab0, int 1) line 970 + 13 bytes
operator delete(void * 0x00421ab0) line 351 + 12 bytes
CTest1App::InitInstance() line 54 + 15 bytes

很顯然,這個錯誤是在調(diào)用delete時遇到的,出現(xiàn)在CTest1App::InitInstance() line 54 + 15 bytes之處。我們很容易根據(jù)這個信息找到,是在釋放哪塊內(nèi)存時出現(xiàn)問題,之后,我們只需要根據(jù)這個內(nèi)存的訪問過程確定哪兒出錯,這將大大降低調(diào)試的難度。

實(shí)例四:子類化

子類化是我們修改一個現(xiàn)有控件實(shí)現(xiàn)新功能的常用方法,我們借用實(shí)例一中的Debug對話框工程來演示我過去學(xué)習(xí)子類化的一個故事。我們創(chuàng)建一個缺省的名為Debug的對話框工程,并按照下列步驟進(jìn)行實(shí)例化:

  1. 在對話框資源中增加一個Edit控件
  2. 用class wizard為CEdit派生一個類CMyEdit(由于今天不關(guān)心子類化的具體細(xì)節(jié),因此這個類不作任何修改)
  3. 為Edit控件,增加一個控件類型變量m_edit,其類型為CMyEdit
  4. 在OnInitDialog中增加如下語句:
    m_edit.SubclassDlgItem(IDC_EDIT1,this);

我們運(yùn)行這個程序,會遇到這樣的錯誤:

				
Debug Assertion Failed! Application:C:\temp\Debug\Debug\Debug.exe File:Wincore.cpp Line:311 For information on how your program can cause an assertion failure, see Visual C++ documentation on asserts. (Press Retry to debug the application)

點(diǎn)擊Retry進(jìn)入調(diào)試狀態(tài),我們可以看到調(diào)用堆棧為:

CWnd::Attach(HWND__ * 0x000205a8) line 311 + 28 bytes
CWnd::SubclassWindow(HWND__ * 0x000205a8) line 3845 + 12 bytes
CWnd::SubclassDlgItem(unsigned int 1000, CWnd * 0x0012fe34 {CDebugDlg hWnd=0x001d058a}) line 3883 + 12 bytes
CDebugDlg::OnInitDialog() line 120

可以看出在Attach句柄時出現(xiàn)問題,出問題行的代碼為:

    ASSERT(m_hWnd == NULL);  

這說明我們在子類化時不應(yīng)該綁定控件,我們刪除CDebugDialog::DoDataExchange中的下面一行:

    DDX_Control(pDX, IDC_EDIT1, m_edit);
問題就得到解決

總結(jié)

簡而言之,call stack是調(diào)試中必須掌握的一個技術(shù),但是程序員需要豐富的經(jīng)驗(yàn)才能很好的掌握和使用它。你不僅僅需要熟知C++語法,還需要對相關(guān)的平臺、軟件設(shè)計(jì)思路有一定的了解。我的文章只能算一個粗淺的介紹,畢竟我在這方面也不算高手。希望對新進(jìn)有一定的幫助。

posted on 2006-07-11 13:39 楊粼波 閱讀(356) 評論(0)  編輯 收藏 引用


只有注冊用戶登錄后才能發(fā)表評論。
網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            久久成人精品一区二区三区| 欧美日韩精品一区视频| 免费黄网站欧美| 午夜精品久久久久久久久久久久| 99re这里只有精品6| 夜夜嗨av色一区二区不卡| 99国产精品| 亚洲视频每日更新| 欧美一区二区三区日韩视频| 久久久美女艺术照精彩视频福利播放| 久久天堂av综合合色| 欧美电影美腿模特1979在线看| 亚洲黄色免费网站| 美女被久久久| 亚洲精品中文字幕在线| 一区二区三区高清| 久久av一区二区三区漫画| 免费成人毛片| 国产精品美女久久久久av超清| 黄色在线一区| 亚洲精品一区二区三区不| 一区二区欧美日韩视频| 欧美激情亚洲激情| 亚洲毛片在线观看.| 亚洲欧美久久| 欧美成人午夜激情视频| 国产精品久久久久9999高清| 亚洲第一精品夜夜躁人人爽| 亚洲欧美不卡| 亚洲国产一区二区在线| 欧美一区二区在线看| 欧美国产综合视频| 国产精品视频一二三| 91久久夜色精品国产网站| 亚洲一品av免费观看| 久久免费视频这里只有精品| 一本色道久久综合狠狠躁的推荐| 久久久亚洲精品一区二区三区| 国产精品成人国产乱一区| 亚洲国产91精品在线观看| 欧美一级播放| 一区二区欧美激情| 欧美不卡在线视频| 国内一区二区三区在线视频| 亚洲欧美日韩区| 亚洲精华国产欧美| 久久久久久久999| 国产嫩草影院久久久久 | 国产欧美在线| 亚洲五月婷婷| 亚洲精品欧美一区二区三区| 久久夜色撩人精品| 国产亚洲欧洲一区高清在线观看 | 国产精品欧美激情| 99国内精品久久久久久久软件| 你懂的成人av| 久久免费黄色| 在线观看成人av电影| 久久免费国产| 久久久国产一区二区| 99re亚洲国产精品| 欧美不卡福利| 一区二区欧美视频| 亚洲乱码国产乱码精品精98午夜| 欧美成人福利视频| 亚洲精品中文字| 亚洲三级电影全部在线观看高清| 免费成人av在线看| 中文欧美日韩| 亚洲一区欧美一区| 国产日韩精品一区| 久久噜噜亚洲综合| 久久综合久久综合九色| 亚洲欧洲免费视频| 日韩视频专区| 国产精品亚洲人在线观看| 亚洲清纯自拍| 国产农村妇女毛片精品久久麻豆| 欧美在线观看视频| 久久福利精品| 亚洲激情av| 亚洲免费激情| 国产麻豆综合| 久热精品视频在线| 欧美激情一区二区三级高清视频| 一区二区三区鲁丝不卡| 亚洲性视频网站| 国内精品视频在线观看| 欧美高清视频| 国产精品九色蝌蚪自拍| 卡一卡二国产精品| 欧美日韩免费高清| 久久精品理论片| 欧美国产视频一区二区| 欧美一区二区三区视频在线| 久久躁狠狠躁夜夜爽| 在线中文字幕一区| 欧美一区二区三区婷婷月色| 亚洲国产视频a| 99热这里只有成人精品国产| 国产在线不卡视频| 亚洲精品美女久久7777777| 国产精品日韩二区| 亚洲福利精品| 国产一区二区三区在线观看精品 | 亚洲韩国日本中文字幕| 亚洲精品中文字幕在线| 韩国v欧美v日本v亚洲v| 亚洲日本va在线观看| 国产字幕视频一区二区| 亚洲精品一二区| 激情久久久久久久| 宅男噜噜噜66一区二区66| 亚洲国产精品一区二区www| 亚洲天堂av在线免费| 亚洲黄色成人| 欧美在线不卡视频| 亚洲欧美国产77777| 欧美精品激情| 免费在线成人av| 国产一本一道久久香蕉| 亚洲无线观看| 妖精视频成人观看www| 免费在线成人av| 美女黄网久久| 国产综合激情| 欧美一区二区精品久久911| 亚洲已满18点击进入久久| 男女av一区三区二区色多| 老司机成人网| 激情久久久久久久| 久久久精品2019中文字幕神马| 久久精品免费电影| 国产日产欧产精品推荐色 | 亚洲视频综合| 在线视频你懂得一区| 米奇777在线欧美播放| 久久亚洲精品一区二区| 国产欧美丝祙| 香蕉久久一区二区不卡无毒影院| 亚洲影院高清在线| 欧美日韩亚洲一区二区三区在线观看 | 美女精品自拍一二三四| 美女91精品| 亚洲激情视频在线观看| 美女啪啪无遮挡免费久久网站| 欧美成人一区二区三区| 最新成人av网站| 欧美精品麻豆| av成人免费观看| 亚洲一区免费在线观看| 国产精品视频精品视频| 欧美一级日韩一级| 久久综合伊人77777| 91久久中文| 欧美色区777第一页| 亚洲午夜精品久久久久久浪潮| 小黄鸭精品aⅴ导航网站入口 | 一区二区欧美日韩视频| 国产精品久久久久久av下载红粉 | 亚洲国产美女精品久久久久∴| 亚洲精品少妇30p| 国产精品啊v在线| 欧美一区二区三区播放老司机| 免费国产自线拍一欧美视频| aⅴ色国产欧美| 国产欧美日韩不卡| 久久久97精品| 99re国产精品| 麻豆精品视频在线| 中日韩午夜理伦电影免费| 国产色婷婷国产综合在线理论片a| 久久色在线观看| 日韩一区二区久久| 老司机精品导航| 午夜精品国产| 亚洲人成网站999久久久综合| 国产精品久久久久久亚洲毛片| 久久久亚洲午夜电影| 中国av一区| 国产精品入口麻豆原神| 欧美一级淫片aaaaaaa视频| 亚洲高清在线视频| 久久黄色级2电影| 中文欧美字幕免费| 亚洲激情视频在线观看| 国外成人在线视频| 国产精品久久久久久久9999| 久久综合五月| 午夜精品视频在线观看| 亚洲免费观看视频| 欧美激情 亚洲a∨综合| 久久激情视频免费观看| 毛片精品免费在线观看| 亚洲女人天堂成人av在线| 亚洲精品午夜| 在线精品国精品国产尤物884a| 国产精品亚洲综合色区韩国| 欧美日韩国产欧美日美国产精品| 久久久噜噜噜久久狠狠50岁|