当前位置: 首页 > news >正文

实用调试技巧(1)

在这里插入图片描述

  • 什么是bug?
  • 调试是什么?有多重要?
  • debug和release的介绍。
  • windows环境调试介绍。
  • 一些调试的实例。
  • 如何写出好(易于调试)的代码。
  • 编程常见的错误。

什么是Bug
我们在写代码的时候遇到的一些问题而导致程序出问题的就是Bug,世界上的第一个Bug是一支飞蛾,这就是Bug的由来,在早期的时候,机器突然坏了,工作人员进行检查,最后发现是一只飞蛾导致机器故障,所以也有了现在的Bug之称。
在这里插入图片描述

当天的工作人员将他贴在了笔记本上,这就是时间上的第一个Bug。
调试是什么
我们在初学的时候会遇到各种各样的问题,这个时候我们就需要进行调试,这样才能解决Bug,所以调试非常重要,一个出色的程序员必须会调试

所有发生的事情都一定有迹可循,如果问心无愧,就不需要掩盖也就没有迹象了,如果问心有愧,就必然需要掩盖,那就一定会有迹象,迹象越多就越容易顺藤而上,这就是推理的途径。
顺着这条途径顺流而下就是犯罪,逆流而上,就是真相。

但是我们在初学的时候总是盲目的调试,可能只是简单摁下键盘上的F10,调试一直到我们代码结束的时候,只是走了一遍流程,却不知道问题在哪里,这就是我们初学者经常出现的问题。
所以我们要拒绝迷信调试,要真正的找到问题所在,解决这个臭虫

2.1 调试是什么?
调试(英语:Debugging / Debug),又称除错,是发现和减少计算机程序或电子仪器设备中程序错误的一个过程。

2.2 调试的基本步骤

  • 发现程序错误的存在
  • 以隔离、消除等方式对错误进行定位
  • 确定错误产生的原因
  • 提出纠正错误的解决办法
  • 对程序错误予以改正,重新测试

而我们写程序的时候出现Bug生活中一般有三种人,一是程序员自己,二是测试员,三是用户。
测试员就是相当于你写的代码经过relase之后到测试员那里,它进行测试。

3 Debug和Release的介绍

Debug 通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序。

就是相当于我们平常在编译器上写的代码

Release 称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优的,以便用户很好地使用。

这个就是我们的代码经过优化后呈现给用户的

那我们用代码给大家演示一下他们有什么不一样的地方

#include<stdio.h>
int main()
{int arr[10] = { 1,2,3,4,5,6 };int i = 0;for (i = 0; i < 10; i++){arr[i] = -1;}for (i = 0; i < 10; i++){printf("%d ", arr[i]);}return 0;
}

在这里插入图片描述
上面就是我们在Debug和release下生成的可执行文件

在这里插入图片描述
在这里插入图片描述
很明显Release下的内存明显小了很多,说名它进行过优化,我们这是在X86平台下,就是32位机器下,当然我们改成X64也是一样的道理

在这里插入图片描述

在这里插入图片描述
3. Windows环境调试介绍
那我们在了解环境之前先要来了解一些东西,比如就是快捷键,何为快捷键,就是让你变得快捷起来。

在这里插入图片描述
在我们的调试里头就有这些快捷键,来介绍几个常用的
CTRL + F5

这个键就是我们每次运行要得到我们的结果的时候用的,它的意思就是开始执行不调试,直接运行程序。

F11

这个就是一条一条语句往下执行,我们在调试过程中这是必然不可缺少的,它也可以进到我们的函数当中去。

F10

逐过程,通常用来处理一个过程,一个过程可以是一次函数调用,或者是一条语句

F10和F11其实差不多,就是后者更细致一点,作用都是差不多的。

F9

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

F5

启动调试,经常用来直接跳到下一个断点处.

下面举例子给大家看看断点有多方便。

在这里插入图片描述

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

我们在上面设置了一个断点,并且条件是i==6,那让我们按下F5看看吧。
在这里插入图片描述
上面我只按了一个F5就到了,可是如果我按F10,要经过一个半的for循环,有时候按的快的时候,一下子按过头了又要重新按(小编一开始就是这么蠢,大家学聪明点),可见断点和F5连用是这么方便。
在这里插入图片描述
同时屏幕上也打印了6个-1
还有其他相当多的快捷键大家也可以去网上找找,增加对VS的理解

会快捷键之后我们还需要学习一些其他的,往下看
3.3 调试的时候查看程序当前信息
在这里插入图片描述

在窗口里有各式各样的好东西,让我们来看看吧
当然我们这些窗口必须是要开始调试起来的时候才能看到,我一开始学的时候,不知道这个,废了好大劲才找到我原来都没有开始调试。
监视窗口
在这里插入图片描述
任意选一个都可以,在这里我们就可以输入我们想要监视的,比如数组名,还有数组的元素,一些变量,我们都可以看到它时刻的值,当然大家也可以看到自动窗口和局部变量,这些也都是监视的,只不过它是自己生成的,虽然很方便但是它有时候会自己变值,总的来说还是不便于观察,我们在学得时候,小编建议大家还是自己尝试,这样才能提升自己的调试技巧。
内存窗口
这个也很重要,我们在栈上创建变量的时候,都是占用空间的,而我们会用相应的编号找到他们的位置,这也是让我们更好的观察他们,让我们更容易调试,找到Bug。

3.3 查看调用堆栈

调用堆栈主要是反映逻辑关系,比如我们过多的调用函数的时候不明白它的逻辑的时候可以使用

举个例子

#include<stdio.h>
void test1()
{printf("hehe\n");
}
void test()
{test1();
}
int main()
{test();return 0;
}

在这里插入图片描述
我们返回的时候是下面这样
在这里插入图片描述
啥意思呢,就是我们之前讲过函数栈帧一样,栈是一步一步在顶上开辟,然后一步一步返回的。

3.4 查看汇编信息

在这里插入图片描述
在函数栈帧讲过,建议大家去看一下
3.5 查看寄存器信息
在这里插入图片描述
之前讲过esp和ebp还有edi这些,他们的作用都是一样,存储数据,其中ebp和esp有维护函数栈帧的作用。

接下来给两个例子给大家调试一下看,不过都是最基础的调试.

实例

实现代码:求 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;
}

我们的代码主要是为了计算阶乘的大小,前几个阶乘相加计算出结果,但是我们上面的代码其实是有问题的,我们一步一步调试来看看,首先我们要写出我们每次心里想的结果去对比编译器中监视进行对比,找出问题。
在这里插入图片描述
在这里插入图片描述

调试之前可以像我一样把要观察的写出来

在这里插入图片描述
j循环的for语句执行一次,因为i=1,算出1的阶乘,答案是对的。
我们看2的阶乘
在这里插入图片描述

在这里插入图片描述
其实我们在这里就能看出结果,原因是我们是在原来的基础性上阶乘,但是运气好,答案是对的,当我们继续往下走,就会看出端倪

在这里插入图片描述
一看和我们预想的有问题,这时候就要警惕,如果我们第一次没看出来,但是结果不一样,这时候就要小心了,不过很显然,为什么造成这个原因,我们在调试过程一看就才可以看出来了,ret一直是在原来基础上改变,所以我们给他进入循环的时候变成1就可以了。

改正

#include<stdio.h>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;ret = 1;for (j = 1; j <= i; j++){ret *= j;}sum += ret;}printf("%d\n", sum);return 0;
}

实例two

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

想要解释这道题必须调试才能知道
给个前提,必须在VS这个编译器下,而且是X86,Debug下,别的一些编译器也行,但还是有点初入,这里我们不进行调试是做不出来,下面调试。

在这里插入图片描述

当我们的i变成9的时候,刚刚把原始数组都赋值为0

在这里插入图片描述
很神奇,我们数组下标到9就应该结束,但是现在竟然把arr[10]的内容赋值为0,我们语句越界访问了

在这里插入图片描述
11也是,i还在变大
在这里插入图片描述
arr[12]怎么放的是i的值,好奇怪,我们继续往下。
在这里插入图片描述

i突然变成0了,arr[12]的内容也是0,说明赋值成功,但是i又变了。我们就要去怀疑他们是不是公用一个空间

在这里插入图片描述
结果一看还真是,那就说明arr[12]和i用的是一个地址,后来i又变成0了,所以才会死循环,讲到这里大家肯定明白了把


在这里插入图片描述
说面我们arr数组开辟的时候和i中间差两个整型,我们数组的下标是由低地址到高地址的

今天我们先讲一部分调试技巧,内容太多了,小编写不动了,谢谢大家!!!

http://www.lryc.cn/news/103305.html

相关文章:

  • uniapp:H5定位当前省市区街道信息
  • 自然语言处理从入门到应用——LangChain:提示(Prompts)-[提示模板:部分填充的提示模板和提示合成]
  • 论文笔记--GloVe: Global Vectors for Word Representation
  • day57|● 647. 回文子串 ● 516.最长回文子序列
  • docker compose.yml学习
  • 【业务功能篇55】Springboot+easyPOI 导入导出
  • 对顶堆算法
  • node.js的优点
  • golang编译跨平台
  • 关于Spring的bean的相关注解以及其简单使用方法
  • 【计算机视觉】BLIP:源代码示例demo(含源代码)
  • TWILIGHT靶场详解
  • 【案例】--GPT衍生应用案例
  • Sip网络音频对讲广播模块, sip网络寻呼话筒音频模块
  • leetcode1219. 黄金矿工(java)
  • Svelte框架入门
  • 在linux中进行arm交叉编译体验tiny6410裸机程序开发流程
  • SpringBoot实战(二十三)集成 SkyWalking
  • 深度学习实践——卷积神经网络实践:裂缝识别
  • linux | vscode | makefile | c++编译和调试
  • Spring | Bean 作用域和生命周期
  • 培训(c++题解)
  • ansible-playbook编写 lnmp 剧本
  • 需求太多处理不过来?MoSCoW模型帮你
  • Vue 3:玩一下web前端技术(六)
  • 【点云处理教程】00计算机视觉的Open3D简介
  • Windows10系统还原操作
  • Django学习笔记-模板(Template)基础
  • 使用 NVM(Node Version Manager)管理 Node.js 版本
  • (文章复现)梯级水光互补系统最大化可消纳电量期望短期优化调度模型matlab代码