程序调试技巧
1.什么是bug
导致程序出错或者无法正常工作的问题或者错误。
2.调试
2.1 调试是什么?
调试又称出错,是发现和减少计算机程序和电子仪器设备的程序错误的一个过程。
2.2调试的基本步骤
1.发现程序错误的存在
2.以隔离、消除等方式对错误进行定位
3.确定错误产生的原因
4.提出纠正错误的解决方法
5.对程序错误予以改正,重新调试
2.3 Debug和Release的区别
Debug通常为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序。
Release称为发布版本,它往往是对代码进行了各种优化,使得程序在大小和运行速度上都是最优的,以便用户很好地使用
我们在写程序时要用Debug版本来写,方便发现程序存在的问题。
调试快捷键
f5 :启动调试,经常用来直接跳到下一个断点出。如果断点在循环里,可能会多次停在这个断点,因为f5是跳到下一个代码执行逻辑上的断点,这时我们可以设置断点的参数来控制在循环的哪一层停下来。断点也可以设置在函数里面。
f9 :创建或取消断点。 断点的重要作用:可以在程序的任意位置设置断点,这样就可以使得程序在想要的位置随意停止执行,继而一步步执行下去。断点还能设置条件,这时就成了条件断点,比如在循环中设置断点的条件。
f10 :逐过程通过,用来处理一个过程,一个过程可以是一次函数调用,或者是一条语句。
f11:逐语句,就是每次执行一条语句,但是这个快捷键可以使我们的执行逻辑进入函数内部(这个功能是最常用的)。而f10遇到函数会直接执行完,当成一条普通的语句,无法看到内部的细节。
ctrl+f5 :开始执行不调试,如果你想让程序直接运行起来而不调试就可以用这个快捷键。
3.调试的时候查看程序当前信息
在上面我们讲到了如果调试,当我们想在调试的过程中查看当前程序中的一些变量或者表达式的值时,我们就需要用到一些窗口。
3.1 查看临时变量的值
进入调试之后,在调试的窗口中找到监视,可以监视变量的值。
数组传参的时候,传过去的是首元素的地址,这时在监视窗口看形参的话,只能看到一个元素,这时候可以输入形参指针加逗号加数字,这样就能看到数组的前几个元素。
3.2 查看内存
当我们想要查看变量或者数组的内存时,我们可以打开调试窗口的内存来查看。查看的时候可以把列改成4,更方便观察,一列就是一个字节的空间。
3.3 查看调用堆栈
这个功能能让我们看到函数调用的逻辑,打开调试,打开窗口中的调用堆栈。我们可以显示外部代码,就可以看到整个程序的函数调用逻辑了。
3.4 查看寄存器
3.5 查看反汇编
这两个功能我们在函数栈帧中用到的十分频繁,可以观察到代码的具体实现还有寄存器的状态。
4.如何写出好(易于调试的代码)
1.使用assert,assert()是断言,如果括号里的表达式为假,assert就会报错,在指针传参的时候我们都可以在函数里用assert判断一下是不是空指针。头文件是
2.尽量使用const。在有些函数比如直接实现strlen时,当只要求长度时,我们传过去的参数(字符串、指针)里的内容是不能修改的,这时候在函数定义参数时用const修饰,我们对确认不能改的量用const修饰,如果对const修饰的对象修改,能在编译的时候就报错。
对于const的使用需要注意的地方:
如果我们这样写
const int a=20;这时候就不能直接对a进行修改,比如a=10;这种修改编译器会报错。但是我们可以绕开a,通过a的地址对他进行修改,int* p=&a; *p=10;这样做就绕开了定义a的const。我们要防止这种情况,这样写 const int* p=&a;这样我们通过解引用p来对a进行更改也会报错,避免了前面的漏洞。
这里就涉及到了一个知识点,const修饰指针,在上面的 int* p=&a这行代码中,const有三个位置可以放,分别是 const int* p=&a; int const * p=&a; int* const p=&a;
前面两种写法const写在了 * 的前面,这时候const修饰的是 *p ,意思是p指向的对象不能通过p来改变了,但是p自身的值是能改变的,p可以改成指向其他变量的指针,但是都不能通过p来更改。
后一种写法const在 * 的后面,修饰的是p的值,意思就是p自身的值是不能更改的,但是可以通过解引用p来修改p所指向的对象。
也可以用两个const:const int* const p=&a;这样的话p的值不能修改了,也不能通过p来修改a的值。
编程常见的错误
1.编译型错误(语法错误,出现在编译期间)
2.链接型错误(出现在链接期间,一般都是标识符名不存在或者拼写错误)
3.运行时错误(通过调试来解决)