VS实用调试技巧
1.概念介绍
(1)bug指程序漏洞,调试(debug)是寻找程序中存在的问题的过程
(2)
Debug通常称为调试版本 ,包含调试信息,不做任何优化,便于程序员调试程序
Release称为发布版本,往往进行了各种优化,不包含调试信息,便于客户使用
2. VS调试快捷键
(1)环境准备:应将开发环境设置为Debug,支持调试
(2)常用快捷键
F9:创建断点和取消断点
打上断点可以使程序执行到想要的位置暂停执行,再用F10,F11快捷键观察代码的执行细节
条件断点:满足某条件,触发断点
F5:启动调试
用于跳到执行逻辑的下一个断点处,一般和F9配合使用
F10:逐过程
通常用来处理一个过程,一个过程可以是一次函数调用,或者一条语句
F11:逐语句
每次执行一条语句,可以使执行逻辑进入函数内部
Ctrl+F5:开始执行不调试
3.监视和内存观察
(1)监视
开始调试后,在菜单栏中【调试】>>【窗口】>>【监视】,打开任意一个监视窗口,输入想要观察的对象即可
(2)内存
观察变量在内存中的存储情况,在菜单栏中【调试】>>【窗口】>>【内存】
4.调试举例
在VS2019,X86,Debug的环境下,编译器不做任何优化的情况下,下面代码执行的结果是什么?
程序进入死循环,经过调试发现,arr[10]和arr[11]和arr[12]可以被赋值为0,arr[12]和i同步变化,且arr[12]和i共用一个地址,造成死循环
原因分析:栈区的默认使用习惯是先使用高地址,再使用低地址(不绝对,比如在VS上切换到X64和Release版本中使用顺序是相反的),所以变量i的地址是较大的,arr数组的地址整体小于i的地址,数组在内存中的存放是随着下标的增长,地址由高到低变化,随着数组下标的增长,数组元素越界覆盖到i,造成死循环
5.编程常见错误
(1)编译型错误:一般是语法错误
(2)链接型错误:一般是因为标识符名不存在,拼写错误,没包含头文件,引用的库不存在
(3)运行时错误:需要借助调试逐步定位问题
函数递归
1.递归的概念
递归就是函数自己调用自己,递就是递推,归就是回归,递归存在限制条件,当满足这个限制条件时,递归便不再继续,每次递归调用后都越来越接近这个限制条件
2.举例
(1)求n的阶乘
n!=n*(n-1)!
(2)顺序打印一个整数的每一位
3.递归与迭代
(1)递归函数在调用的时候需要考虑运行时的开销
在C语⾔中每⼀次函数调⽤,都需要为本次函数调⽤在内存的栈区,申请⼀块内存空间来保存函数调⽤期间的各种局部变量的值,这块空间被称为运⾏时堆栈,或者函数栈帧。函数不返回,函数对应的栈帧空间就⼀直占⽤,所以如果函数调⽤中存在递归调⽤的话,每⼀次递归
函数调⽤都会开辟属于⾃⼰的栈帧空间,直到函数递归不再继续,开始回归,才逐层释放栈帧空间。所以如果采⽤函数递归的⽅式完成代码,递归层次太深,就会浪费太多的栈帧空间,也可能引起栈溢出的问题
如:计算阶乘的问题用迭代比用递归效率更高
事实上,我们看到的许多问题是以递归的形式进⾏解释的,这只是因为它⽐⾮递归的形式更加清晰,但是这些问题的迭代实现往往⽐递归实现效率更⾼,当⼀个问题⾮常复杂,难以使⽤迭代的⽅式实现时,此时递归实现的简洁性便可以补偿它所带来的运⾏时开销
(2)举例:求第n个斐波那契数列
由此可见,使用递归的方式,第三个斐波那契数被重复计算了 39088169次,这些计算是非常冗余的,运行时间比较长,但如果使用迭代的方式,效率会高很多: