欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  数据库

VC调试技巧

程序员文章站 2024-01-30 13:07:28
...

编译器基本技巧: F10: 单步调试,按步执行程序,一般用来察看程序执行流程,如果程序程序从中断掉了,就可以用单步调试。 F9: 设置断点,程序在执行到设置断点的地方就会停下。 F5: 执行调试程序,Debug|Go。 F11: 进入block内部进行调试。 Ctrl+F5: 在使用

编译器基本技巧:

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 theexpression to be evaluated"这一项中输入一个表达式,这个表示只有在该表达式成立的情况下,这个断点才能被启动.当然如果是输入一个变量名的话,那么在这个变量被修改的时候,断点才能被启动.另外还有两项:"Enter the number of elements to watch in an arrayor structure" 以及"Enter the number of times to skip before stopping".在这里都可以设置一些选项,这些对于循环的block非常有用.第一项表明要输入某个数组或者结构中要观测的项目数,第二项表示在终止前要跳过的次数,显然在循环次数很多的情况下是很有用的,在这里我们可以设置他为循环的次数,这样的话,等所有信息都设置好以后,按F5以后,程序就会被中断,那么我们在"breakpoints"这里就可以看到一个信息"remainingxx"这表示说在循环进行100-xx+1项后就终止了,我们当然还可以采用"Enter theexpression 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)在释放内存以后,该指针仍旧未非空的,所以还要将之置空,避免出现野指针。