目录
我们要知道什么是调试首先要知道什么是bug
一.什么是bug
bug,简称程序漏洞,现在⼀般是指在电脑系统或程序中,隐藏着的⼀些未被发现的缺陷或问题,简称程序漏洞。或计算机软硬件可能存在的问题。
二.什么是调试
当我们发现程序中存在的问题的时候,那下⼀步就是找到问题,并修复问题。承认程序有问题,这个找问题的过程称为调试。
总结1:
(1)调试是为了方便程序员,找到代码的bug或错误从而改成//所以要养成调试的习惯。
(2)但是不能胡乱调试。
上面只需要了解一下面才是重点
三.Debug和Release
(1)Debug通常称为调试版本。程序员能调试的版本。(包含调试信息,并且不做任何优化)。
(2)Release称为发布版本。程序已经没有缺陷,可以交付使用,用户使用的版本。(不包含调试信息,用户不包含调试)。
差异:Debug和Release:文件大小不一样区别
因为Debug为了方便我们调试代码,包含调试信息,Release不包含调试,所以文件大小不一样。
1.如何改成Debug和Release版本?
总结2:
1.Debug它包含调试信息,并且不作任何优化,便于程序员调试程序。因为程序员在写代码的时候,需要经常性的调试代码。
2.Release它不包含调试信息,进⾏了各种优化,使得程序在代码⼤⼩和运⾏速度上都是最优的,以便⽤⼾很好地使⽤。因为用还不需要调试
3.区别文件大小不一样。
4.Releas版本的代码可能做过优化的,编译器自作主张进行优化。
四.vs快捷键
1.调试最常使⽤的⼏个快捷键
F9:创建断点和取消断点。一个红色的点.
断点的作⽤是可以在程序的任意位置设置断点,打上断点就可以使得程序执⾏到想要的位置暂定执⾏,就可以使⽤F10,F11这些快捷键,观察代码的执⾏细节。
断点在循环里面使用,先在循环里面设置断点,右击鼠标点条件,然后就可以设置断点条件,来到循环几
条件断点:满⾜这个条件,才触发断点
F5:启动调试,经常⽤来直接跳到下⼀个断点处,⼀般是 和F9配合使⽤。
F10:逐过程,通常⽤来处理⼀个过程,⼀个过程可以是⼀次函数调⽤,或者是⼀条语句。
F11:逐语句,就是每次都执⾏⼀条语句,但是这个快捷键可以使我们的执⾏逻辑进⼊函数内部。在函数调⽤的地⽅,想进⼊函数观察细节,必须使⽤F11,如果使⽤F10,直接完成函数调⽤。
CTRL + F5:开始执⾏不调试,如果你想让程序直接运⾏起来⽽不调试就可以直接使⽤。
VS更多快捷键了解:http ://blog.youkuaiyun.com/mrlisky/article/details/
2.代码举例
(1)F5和F9使用举例代码
F9设置断点,再按F5进去
会直接打印5个hehe,不需要一个一个按F10
1.假设这个代码有1000行代码,断点哪里是500行代码,通过分析前面500行代码没错,按下F10调试开始再按下F5它会直接来到断点处。
2.先按下F10开始调试,来到程序mian函数第一行,一行代码必须执行之后才会实现,不是来到当前一行代码实现的。
3.F9告诉你这里可以停下来,F5告诉你直接去那里。
4.避免无效调试
五.监视和内存观察
1.监视
要先F10调试-->再在调试里面找窗口-->再找监视就能监视每个值。看图
可以选择4个窗口都可以
2.内存
要先F10调试--再在调试里面找窗口--再找内存就能观察每个地址
用法:
1.观察数组的内存的地址,直接输入数组名,数组名本来就是地址。
2.观察其它数据,要用取地址操作符。
3.内存和监视要一起使用,程序要一步一步执行,并把值赋予了,才能看到内存地址。
4.内存地址是16进制,因为每8个bit为1字节,最好改成1行显示4列,方便程序员观察。
输入你要观察的地址就能查看但必须内存和监视一起用,4列是为了观察我们地址,可以自己改一下;
补充:不管什么数据在内存中存储的是二进制形式,因为二进制太长,因此为了程序员方便观看内存的变化显示的十六进制
六.调试举例
1.求n的阶乘
假如n = 3,把1的阶乘,2的阶乘,3的阶乘加起来等于多少
这是一个陷阱代码你可以试试你自己看的出来吗?假如n = 3,把1的阶乘,2的阶乘,3的阶乘看看是不是你心中所想的值。
先把你想观察的值输入好,输入好了为啥无法读取内存,因为你就调试,程序还没走,还要按F10或者F11才会慢慢有值,让程序一步一步走起来。
现在有值,该F11进入循环
调试总要自己动手才行,方法就这样,自己改正错误,最后n =3,sum = 9。
七.面试题和栈区一点点讲解
只能在在vs,x86,debug的环境下才会,有点问题,其它编译器有差异!
看一下边代码,有什么问题?
如果越界了会不会打打印hehe,还是会程序崩坏掉?
当我们运行这个程序,居然会一直死循环打打hehe
但明明下标都越界了,程序会崩掉,但没有崩掉这个程序会死循环打印hehe,为什么?
好好好,只会不知道是吧,那就调试一下吧!
通过图片发现越界访问的下标元素的值都被修改为0,只能说这代码胆子太大了!但访问到下标12的时候i的值也为了0所以一直死循环,这点就需要了解栈的知识了。
补充一下知识点
数组在内存中是连续存放的,随着下标的增长,地址是由低到高变化的!这很重要。
1.栈的小知识
(1)栈区内存的使用习惯默认是从高地址向低地址使用的,所以先创建的地址大,后创建的地址小。所以变量i的地址较大的。arr数组的地址整体小于i的地址。
(2)因为栈区使用地址的习惯,i的地址高,又因为随着下标的增长,地址是由低到高变化的,当使用到高地址就慢慢接近i的地址,
越界访问恰好访问到i的地址,在同一块空间,所以当访问下标12数组的元素,i和数组元素一起修改为0,所以会一直循环。
(3)内存分为3个区堆区,栈区,静态区
忙着死循环,不会报错
总结:这是巧合,在不同的编译器,编译环境不同,到底空几格是不一定的.
避免死循环
3.补充栈区知识
在vsx86环境Dubag版本
a地址>b地址;根据栈默认使用地址习惯先使用高地址后使用低地址。
但在vsx64环境Dubag版本,不同
总结:栈区空间,是默认先使用高地址,再使用第地址,具体看编译器怎么实现。
2.学习调试重要性
(1)所以调试非常的重要,只有调试才知道这些问题。
(2)调试过程中,要做到心中有数,心里要清晰,希望代码怎么执行的,
然后再去看有没有按照我们预定的路线在主席那个。
(3)调试是需要反复去动手练习的,调试是可以增加程序员对代码的理解和掌控的
掌控了调试,就能看到本质。
(4)不要害怕编程出错
7.编程常见错误归类;分3类
(1)编译型错误 -- 检查语法错误/最简单
指缺少分号,括号,逗号这些等
7.2链接型错误
指标识符号不存在,拼写错误,比如关键字写错,printf写错,自定义函数名字写错,
头文件没包含,引用库不存在
LNK无法解析的外部符号,基本上都是链接错误
7.3运行时错误 -- 最可怕的
调试解决的是运行时的问题;自己希望结果和编程不一致
.