【C语言不能不会的操作】调试-万字详解【windows操作系统下】(会写bug还会调试解决bug的程序员简直帅呆了,赶紧点赞收藏)

目录

1. 什么是bug?

 2. 调试是什么?有多重要?

2.1调试是什么 

2.2 调试的基本步骤

2.3 Debug和Release的介绍

3. Windows环境调试介绍 

3.1 调试环境的准备

3.2 学会快捷键 

​编辑 3.3更多的快捷键

3.4 调试的时候查看程序当前信息 

3.4.1 查看临时变量的值 在调试开始之后,用于观察变量的值。

 3.4.2 查看内存信息

3.4.3 查看调用堆栈 

3.4.4 查看汇编信息 

3.4.5 查看寄存器信息 

4.调试实例 

4.1

4.2 

 5.优秀的代码特点

6.常见的编码技巧 

6.1assret(断言的使用)strcpy函数实现 

 6.2const的作用

7. 编程常见的错误

7.1 编译型错误

7.2 链接型错误 看错误提示信息

7.3 运行时错误

8.结语


1. 什么是bug?

那我们就不得不提及那个开拓时代,和那段在编程世界的神话;

与“阿兰·图灵、史蒂夫·乔布斯、比尔·盖茨”等一同入选“IT界十大最有远见的人才”的唯一一位女性——格蕾丝·赫柏

 大家可以点击链接了解她的故事,现在我们来看一下她是如何发现并命名世界上第一个“BUG”的。

赫柏从海军学校毕业后,因为出色的数学背景,被分配到美国船舶局位于哈佛大学的战时科研中心。彼时,军方正在开展世界第一台大型数字计算机的研究项目——马克一号。赫柏被任命为著名计算机专家霍德艾肯博士的助手,成为这个项目的第三名程序员。在这儿,赫柏开启了自己程序员的生涯。她的主要任务,就是为马克一号,还有接下来的马克二号,马克三号编写程序与软件。

 一天,计算机发生故障,赫柏经过排查,在计算机的继电器触电里,找到了一只被夹扁的小飞蛾,这只小虫子卡住了机器的运行,赫柏顺手将飞蛾夹在工作笔记里,并诙谐的把程序故障称为“bug”。这就是我们今天最爱说的“bug”的由来。它的意思,和原身一致,真就是“一只臭虫”这一称呼后来演变成表达缺陷漏洞的计算机专业术语,人们习惯地把排除程序故障叫做“debug”(除虫)。

赫柏发现第一个“bug”后的手稿:

这便是BUG的由来 

 2. 调试是什么?有多重要?

所有发生的事情都一定有迹可循,如果问心无愧,就不需要掩盖也就没有迹象了,如果问心有愧, 就必然需要掩盖,那就一定会有迹象,迹象越多就越容易顺藤而上,这就是推理的途径。 顺着这条途径顺流而下就是犯罪,逆流而上,就是真相。 一名优秀的程序员是一名出色的侦探。 每一次调试都是尝试破案的过程。

我们写代码都是简单粗暴:

所以,更要知道调试的重要性和如何调试代码。而不是能跑就行,特别是在学习阶段。 

2.1调试是什么 

调试(英语:Debugging / Debug),又称除错,是发现和减少计算机程序或电子仪器设备中程序 错误的一个过程。人们习惯地把排除程序故障叫做“debug”(除虫)。

2.2 调试的基本步骤

①发现程序错误的存在

②以隔离、消除等方式对错误进行定位

③确定错误产生的原因

④提出纠正错误的解决办法

⑤对程序错误予以改正,重新测试

2.3 Debug和Release的介绍

Debug 通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序。 Release 称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优 的,以便用户很好地使用。

 ①debug版本支持调试:

②relese版本不支持调试

 

程序感觉一步跳完。

3. Windows环境调试介绍 

3.1 调试环境的准备

3.2 学会快捷键 

快捷键有的电脑可能不起作用,

解决:

1.关掉fn

2.使用Fn+快捷键就行

最常使用的几个快捷键:

F5 启动调试,经常用来直接跳到下一个断点处(这个断点是执行逻辑上的断点,比如我在上述代码的赋值部分设置断点他就会循环一次停一下)。所以要和F9配合使用

F9 创建断点和取消断点 断点的重要作用,可以在程序的任意位置设置断点。 这样就可以使得程序在想要的位置随意停止执行,继而一步步执行下去。

F10 逐过程,通常用来处理一个过程,一个过程可以是一次函数调用,或者是一条语句。 F11 逐语句,就是每次都执行一条语句,但是这个快捷键可以使我们的执行逻辑进入函数内部(这是最 常用的)。如果是遇到要跳转函数,它会进入函数的实现过程去一句句的执行

CTRL + F5 开始执行不调试,如果你想让程序直接运行起来而不调试就可以直接使用。

关于断点的妙用:条件断点

比如上述代码,如果程序出现错误我怀疑是第五次赋值出行了问题,那么我在赋值这一行设置断点是不是要经历5次循环,有点麻烦,我们就利用断点的条件设置。 

 3.3更多的快捷键

1、窗口快捷键
记忆诀窍:

凡跟窗口挂上钩的快捷键必有一个W(Windows);
Ctrl+W,W: 浏览器窗口 (浏览橱窗用有道的翻译是window shopping)

Ctrl+W,S: 解决方案管理器 (Solution)

Ctrl+W,C: 类视图 (Class)

Ctrl+W,E: 错误列表 (Error)

Ctrl+W,O: 输出窗口(输出Output;输出程序的的编译信息 ;可在vs中“工具”—-“选项”—-“调试”—-“输出窗口”进行配置需要查看哪些信息)

Ctrl+W,P: 属性窗口 (属性 Property)

Ctrl+W,T: 任务列表 (任务Task)

Ctrl+W,X: 工具箱 (事实上工具应该是Tool 但t已被任务列表占用了 ;参照一些大神的记忆方法:“X”长得四通八达,而工具就有这样一个特点,所以属性的快捷键是“X”)

Ctrl+W,B: 书签窗口 (书签 Bookmark 书签非常好用,如果有几千行代码,在寻找代码的时候添加书签找起来要快很多)

Ctrl+W,U: 文档大纲 (OutLine;使用第二个字母U)

Ctrl+D,B: 断点窗口 (breakpoint)

Ctrl+D,I: 即时窗口 (immediately)

2、项目功能快捷键**
规律:

ctrl是强制功能键
shift有给项目增加功能
CTRL + F6 /CTRL + TAB下一个文档窗口即活动窗体切换 (windows操作系统是alt+tab表示在任务之间切换

CTRL + SHIFT + F6 /CTRL + SHIFT + TAB上一个文档窗口 (在windows系统操作中 相信大家都知道shift有相反的功能 哈哈 在这里体现了)

F7: 查看代码 (WebForm开发里面,是查看后台代码)

Shift+F7: 查看窗体设计器 (在后台cs文件的时候,这种方式很方便跳转到前台.aspx页面)

Ctrl+Shift+N: 新建项目 (N是New新建的意思 那如果需要强制在项目中新建项目的话 自然就是组合键Ctrl+Shift+N)

Ctrl+Shift+O: 打开项目 (Open)

CTRL + SHIFT + C显示类视图窗口(C代表Class类的意思)

CTRL + F4关闭文档窗口 (相信用过qq的大家都有使用alt+f4来关闭当前聊天窗口 想想用ctrl+tab在活动标签窗口切换就知道为什么关闭当前标签窗口是ctrl+f4)

CTRL + SHIFT + E显示资源视图 (E代表Explorer资源管理器的意思)

CTRL + SHIFT + B生成解决方案 (B代表Build生成的意思 其实用F6也可以实现)

Shift+F6表示生成当前项目 (上面已经说过使用f6可以生成整个解决方案 那如果是当前项目的生成那自然就是加上功能键shift了 )

F4 显示属性窗口 SHIFT + F4显示项目属性窗口

Ctrl+Shift+S: 全部保存 (S代表Save保存的意思 这里表示全部保存是因为如果只是单个保存Ctrl+S跟整个项目没有任何瓜葛 而全部保存的概念应该是说在整个项目中 所以组合键中自然会有shift了哦)

Ctrl+Shift+A: 新建项(A是Add的意思)

Shift+Alt+C: 新建类 (shift是跟项目有关的功能键;Alt用的非常多,空格(用的非常多)旁;C是Class;而且添加类用的非常多;所以自然就是:Shift+Alt+C)

3、查找相关快捷键
Ctrl+F: 查找 (Find) Ctrl+Shift+F: 在文件中查找 (上面已经提过了shift是表示在项目中 所以如果需要在项目中的文件中查找的话 那自然就少不了Shift)
F3: 查找下一个 (相信使用过windows系统的人都知道f3是查找的快捷键)
Shift+F3: 查找上一个 (shift在此有反向的功能哦)
Ctrl+H: 替换
Ctrl+Shift+H: 在文件中替换

4、代码快捷键
Ctrl+E,D(ctrl+k,d) —-格式化全部代码 ;让你的代码瞬间整洁起来。

Ctrl+E,F —-格式化选中的代码(如果你已经记住Ctrl+E+D是格式化全部代码的话 那你想想规律不就知道了吗 F不就在D的右边表示它是特定某一范围)

Ctrl+K,C: 注释选定内容 (Comment)

Ctrl+K,U: 取消选定注释内容 (UnComment)

Ctrl+J /Ctrl+K,L: 智能提示 列出成员 (kernel核心内容 list列表 如果我们想查看一个对象具有的成员具体信息的时候试下这个快捷键吧)

Ctrl+K,P: 参数信息 (kernel核心内容 Parameters参数 如果我们想查看一个方法的具体参数的时候这个组合键可是挺有用的哦)

Ctrl+K,I: 快速查看信息(Infomation)

Ctrl+K,S: 外侧代码(平时个人惯会时不时的用#region 用了region之后代码看起来就特别整洁 所以自然而然的就用**惯了这个)

CTRL + M, CTRL + M 折叠或展开当前方法

CTRL + M, CTRL + O 折叠所有方法

CTRL + M, CTRL + L展开所有方法
CTRL + M, CTRL + P展开所有方法

Ctrl+M,P: 停止大纲显示 (用了region将代码折叠起来之后试试用这组组合键吧 体验一下折叠和展开的**吧 看着舒服的代码我相信你记住这对快捷键肯定是值得的)

ctrl+shift+f10:自动添加using命名空间(在实例化对象的时候,使用的非常多)

5、调试快捷键
F5: 启动调试

Ctrl+F5: 开始执行(不调试)

Shift+F5: 停止调试

Ctrl+Shift+F5: 重启调试

F9: 启用/关闭断点

Ctrl+F9: 停止断点

Ctrl+Shift+F9: 删除全部断点

F10: 逐过程
Ctrl+F10: 运行到光标处
F11: 逐语句

6、编辑快捷键
Shift+Alt+Enter: 切换全屏编辑(如果想一心一意的只写代码 让整个vs铺满全屏 感觉还不错哦)

F12: 转到所调用过程或变量的定义

Alt+F12: 查找符号(列出所有查找结果)

shift+f12:查找所有引用(讲光标放在单词上, 然后按Shift + F12)

Ctrl+U: 全部变为小写 (的sql语句全部转换成大写以提高性能 )

Ctrl+Shift+U: 全部变为大写 (U表示Upper )

Ctrl+Shift+V: 剪贴板循环 (平时我们都只**惯用ctrl+c 和ctrl+v 大家可能还不知道事实上微软都已经帮我们把多次剪切的结果都保存了下来 记下这组快捷键吧 可以粘贴上几次剪切的结果 一用便知道它的强大厉害之处)

Ctrl+Shift+L: 删除当前行 (这个很有用哦 因为大家常常会要删除多余的空行 哈哈 这组快捷键会让你省力不少)

Ctrl+E,S: 查看空白(ctrl+r,w 和它一样可以查看空白或者说显示或隐藏tab标记)

Ctrl+E,W: 自动换行 (代码太长还有滚动条,特别是写append(sql.toString)语句的时候,太长,所以这时候就不得不拖动滚动条,这样以后就可以换行显示了)

Ctrl+G: 转到指定行 (通过情况下 我们想在跳转到具体某一行 用它太方便了)

Shift+Alt+箭头键: 选择矩形文本 Alt+鼠标左按钮: 选择矩形文本

CTRL + DELETE删除至词尾 CTRL + BACKSPACE删除至词头 SHIFT + TAB取消制表符

Ctrl+左右箭头键:一次可以移动一个单词

Ctrl+单击: 选中当前点击的整个单词

SHIFT + END选择至行尾
SHIFT + HOME选择至行开始处

CTRL + SHIFT + END选择至文档末尾 CTRL + SHIFT + HOME选择至文档末尾开始

CTRL + SHIFT + PAGE UP选择至本页前面 CTRL + SHIFT + PAGE DOWN选择至本页后面

CTRL + PAGE DOWN光标定位到窗口上方 CTRL + PAGE UP光标定位到窗口下方

CTRL + END文档定位到最后 CTRL + HOME文档定位到最前

按两下tab快速**代码段(写for, foreach循环,或者try, 还有绑定事件方法)

ctrl+减号:回退到光标上一次的位置

3.4 调试的时候查看程序当前信息 

3.4.1 查看临时变量的值 在调试开始之后,用于观察变量的值。

一定是开始调试之后才能看到这些窗口

 3.4.2 查看内存信息

我们来验证一下数组首元素的地址和数组的地址是一样的

3.4.3 查看调用堆栈 

反馈函数调用关系

 当我们用F11走起来:

3.4.4 查看汇编信息 

点击代码右击转到反汇编就可以看到我们C语言的汇编代码

就可以看到每条代码对应的汇编代码

第二种方式查看我们的反汇编代码;

3.4.5 查看寄存器信息 

4.调试实例 

4.1

实现代码:求 1!+2!+3! ...+ n! ;不考虑溢出。看一下以下实现代码的问题在哪里

int main()
{
 int i = 0;
 int sum = 0;//保存最终结果
 int n = 0;
 int ret = 1;//保存n的阶乘
 scanf("%d", &n);
 for(i=1; i<=n; i++)
 {
 int j = 0;
 for(j=1; j<=i; j++)
 {
 ret *= j;
 }
 sum += ret;
 }
 printf("%d\n", sum);
 return 0;
}

 

期待输出1!+2!+3! = 1+2+6 =9,但实际输出的是15。 why? 这里我们就得找我们问题。

1. 首先推测问题出现的原因。初步确定问题可能的原因最好。

2. 实际上手调试很有必要。

3. 调试的时候我们心里有数 (要清楚的知道自己的代码每一步在干什么)

我们基础调试,就是通过对变量的监视来看是哪一步代码出现问题,尽量选择较小的数据进行测试。现在我们就调试起来看一下:

我们先来看求1的阶乘的过程中有没有错误:

 

目前来看求11的阶乘过程没有问题,结果等都是对的,我们来看求2的阶乘再与1的阶乘相加的过程:

 

当i=2,求2的阶乘的时候ret 和 sum以及各项值都没问题,我们接着走完:

 

找到问题,就代码修改如下:

int main()
{
	int i = 0;
	int sum = 0;//保存最终结果
	int n = 0;
	//int ret = 1;//保存n的阶乘
	scanf("%d", &n);
	for (i = 1; i <= n; i++)
	{
		int ret = 1;
		int j = 0;
		for (j = 1; j <= i; j++)
		{
			ret *= j;
		}
		sum += ret;
	}
	printf("%d\n", sum);
	return 0;
}

 看一下结果:

4.2 

这段代码死循环,请找出死循环原因

#include <stdio.h>
int main()
{
    int i = 0;
    int arr[10] = {0,1,2,3,4,5,6,7,8,9 };
    for(i=0; i<=12; i++)
   {
        arr[i] = 0;
        printf("hehe\n");
   }
    return 0;
}

这道题会死循环的打印hehe,有些编译器是打印第13个hehe的时候就报错警告,但是为了方便调试讲解还是回归这道题目的本意去探究是死循环的原因。那我们调试起来。 

 竟然修改了我们的第1个元素,我们再接着看

我这里应该是编译器的问题,画图讲解时候解释,这道题调试到最后我们实际发现,当第12个元素变为0时我们的i此时也变为0l了,当我们取地址就发现第12个元素和i地址是一样的,所以就会死循环。 ,死循环时虽然也越界但是编译器会忙着循环所以没有报错。

编译器不同数组越界访问后和其他变量之间的间隔也不一样。 

 5.优秀的代码特点

1. 代码运行正常

2. bug很少

3. 效率高

4. 可读性高

5. 可维护性高

6. 注释清晰

7. 文档齐全

6.常见的编码技巧 

1. 使用assert

2. 尽量使用const

3. 养成良好的编码风格

4. 添加必要的注释

5. 避免编码的陷阱。

6.1assret(断言的使用)strcpy函数实现 

关于函数strcpy函数的实现思想和介绍大家可以看我的这篇文章

strcpy函数介绍及模拟实现

但是实现的代码并不完美,我们来看一下。

当我们的用户给我们传入一个空指针,我们对一个空指针解引用,程序就崩了,我们也不知道是哪里出错了。所以我们需要用assert这个宏来断言一下,传入的是空指针就报错 

包含头文件:assert.h

当我们传入一个空指针时,就会打印告诉我们在那个文件路径底下,程序的哪一行有错误 

他可以判断认为未来的条件,不仅仅不可以断言指针。

 6.2const的作用

上面我们已经实现了我们自己的strcpy,当我们和库函数进行比较:

大家也可以跳入我的这篇博文对const有一个更深入的了解:

const的作用详解

我们发现唯一的不同,就是原字符串的地址库函数用了const修饰,这里的const就是用来保护原字符串的内容不被修改,由于目标字符串内容要变成原字符串的内容所以不用const修饰。我们来看一下有const和没有const修饰导致的结果。 

这里我们加入有程序员实现的时候将源地址和目的地址写反了,导致的结果就是把目的地址写进我们的源字符串去了,我们编译看一下:

虽然有些编译器会报错,但是还是导致了源字符串内容被修改。 

我们再来看看当有const来保护我们的原字符串内容的时候 :
编译器直接不允许通过编译

7. 编程常见的错误

7.1 编译型错误

直接看错误提示信息(双击),解决问题。或者凭借经验就可以搞定。相对来说简单。编译错误是在编译期间产生的错误,是语法错误。

7.2 链接型错误 看错误提示信息

主要在代码中找到错误信息中的标识符,然后定位问题所在。一般是标识符名不 存在或者拼写错误。链接期间产生的错误

7.3 运行时错误

比如上述的起阶乘的错误。

借助调试,逐步定位问题。最难搞。

温馨提示: 做一个有心人,积累排错经验。 

8.结语

  以上就是本期的所有内容,知识含量蛮多,大家可以配合解释和原码运行理解。创作不易,大家如果觉得还可以的话,欢迎大家三连,有问题的地方欢迎大家指正,一起交流学习,一起成长,我是Nicn,正在c++方向前行的奋斗者,感谢大家的关注与喜欢。

  • 62
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值