VC++调试技巧

VC++调试技巧
 

编译器基本技巧:

 

F10: 单步调试,按步执行程序,一般用来察看程序执行流程,如果程序程序从中断掉了,就可以用单步调试。

F9: 设置断点,程序在执行到设置断点的地方就会停下。

F5: 执行调试程序,Debug|Go

F11: 进入block内部进行调试。

Ctrl+F5: 在使用的时候,执行调试程序,Debug|Execute

Ctrl+F7: 编译单个文件,而不编译所有文件,这样可以避免编译一些不必要的文件而增加编译时间。

Clean: 清除工程

Rebuild all : 删除之前产生的中间生成文件以后,重新编译整个工程

Watch 窗口:将变量添加到watch窗口,并且可以查看变量值,但是不能察看函数的返回值,一般要看函数的返回值,应该将其保存在变量,然后进行察看。

Call Stack: 调用函数栈,在call stack里面可以看到各级函数的调用关系,并且这样方便定位bug报错位置。

Shift+F5: 取消调试过程。

Ctrl+ B: 设置数据断点和位置断点。

 

 

位置断点:


这是我们经常采用的办法,在我们要调试的代码行上设置一个断点,然后按F5或者F10进行调试,这种方法在非循环的代码block里面是可行的,但是如果在一个循环里面,那么就有点麻烦了,这样的话,你要多次地按F5,假如循环次数超过1000的话,想想看,不说你按F10来单步调试,就是按F5也要耗掉你不少体力.这里介绍一个方法:
即断点的条件判断法.
设置你要调试的代码行.
ctrl+b打开断点设置对话框,这样的话,我们就看到了一个location,我们可以在下面的breakpoints列表里选择我们所要调试的代码行,这样的话,在上面我们就可以在break at这个框里面看到我们刚才选择的代码行,接着在下面的condition按钮中选中,"Enter the expression to be evaluated"这一项中输入一个表达式,这个表示只有在该表达式成立的情况下,这个断点才能被启动.当然如果是输入一个变量名的话,那么在这个变量被修改的时候,断点才能被启动.另外还有两项:"Enter the number of elements to watch in an array or structure" 以及"Enter the number of times to skip before stopping".在这里都可以设置一些选项,这些对于循环的block非常有用.第一项表明要输入某个数组或者结构中要观测的项目数,第二项表示在终止前要跳过的次数,显然在循环次数很多的情况下是很有用的,在这里我们可以设置他为循环的次数,这样的话,等所有信息都设置好以后,F5以后,程序就会被中断,那么我们在"breakpoints"这里就可以看到一个信息"remaining xx"这表示说在循环进行100-xx+1项后就终止了,我们当然还可以采用"Enter the expression to be evaluated"这种方法,直接从值这个方面来获取信息,而不是从次数上获取信息.
for (int i = 100;i >0;i--) {
                printf("%d",100/(i-5));
        }
这就是个例子来的.


数据断点:


就是我们开始说的"Enter the expression to be evaluated"这种方法,即在满足某个条件值时,断点才会启动.
其实位置断点和数据断点是互补的,相对而言,数据断点更加适合于判断数据时候被修改这种的情况,而位置断点一般用在循环中,来说明程序执行的情况.在那个位置出错了,我们找到这个位置再去定位错误.
另外还有一种方法:callstack里面为某个函数设置断点,这样的话,就可以将函数调用从深度层次中返回.
还有可以在调试状态下,在某个代码行A处单击右键菜单中选择set next statement这个项,这样的话,下次执行的代码行就是A,而不是其它代码行,其实从字面意思就可以了解了.
另外watch窗口也可以用来监视变量的值变化,很管用的,特别对于一些数组,结构之类的,你可以查看数组/结构里面所有元素的值情况,不过我们用完了就得删除我们选择的变量,因为下次使用的时候它们还会出现的.

 

打印log:

可以通过输出函数打印相关信息,将log信息输出到屏幕,这些方法主要有:

Prinf, cout, MessageBox, 以及OutputDebugString等。当然也可以将信息记录在文本里面供执行以后察看,用fopen之类的,将信息写入文件。

 

查找崩溃地址:

可以采用dbghelp,具体信息可以到微软的官网上查找

 

也可以使用map 文件,这里有一篇介绍不错的文章。

http://www.codeproject.com/KB/debug/mapfile.aspx

 

使用断言:

断言可以帮助你更加检查数据的有效性,不过要注意的一点是,不要在断言里面使用函数,因为在非debug模式下面,比如release模式下面assert是无效的,要跳过去执行的。

 

使用异常:

Try/catch/throw, 以及__try/__catch/__throw

 

其他调试信息和相关编译器设置信息:

http://ei.cs.vt.edu/~cs1205/c_debug/intro.html

http://www.hermetic.ch/cfunlib/debug.htm

http://www.gamedev.net/reference/articles/article1344.asp

 

编程习惯建议:

多使用断言,assert对数据前驱和后继进行检查,使用防御编程,在函数入口,和函数出口处都进行数据检查。

在申请内存或者分配空间的时候,要检查数据是否分配成功,如果没有分配成功的话,要做相关的处理。

在声明变量的时候,最好也要进行初始化,防止没有经过初始化获得一些未知的数值。

未初始化的内存区一般被0xcccccccc填充,已经释放掉内存的区域一般是0xcdcdcdcd.

如果你试着访问一个数据值为0xcdcdcdcd的指针,那么意味着你已经在某个地方将该之珍释放掉了,如果是0xcccccccc,那么意味着该指针一直没有被分配空间。
 

(1)指针消亡了,并不表示它所指的内存会被自动释放。(摘自林博士的高质量C++编程)

 

(2)内存被释放了,并不表示指针会消亡或者成了NULL指针。

这里可以这样来理解:
1)对于栈上的内存区域,那么在该指针离开了该作用域以后,就无效了,但是并不意味着该指针所占据的内存被自动释放了,相反,出现内存泄漏了。
2)在释放内存以后,该指针仍旧未非空的,所以还要将之置空,避免出现野指针。

你可能感兴趣的:(VC++调试技巧)