实用调试技巧

news2025/6/21 12:58:39

个人主页:平行线也会相交
欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由

目录

  • 什么是bug?
  • 调试是什么?有多重要?
    • 调试是什么
    • 调试的基本步骤
  • debug和release的介绍
  • windows环境调试介绍
    • 快捷键
  • 调试的时候查看程序当前信息
    • 查看临时变量的值
  • 调试实例
    • 实例一
    • 实例二
  • 如何写出好(易于调试)的代码
    • 优秀的代码
  • 编程常见的错误
    • 编译型错误
    • 链接型错误
    • 运行时错误

什么是bug?

在这里插入图片描述
第一次被发现的导致计算机错误的飞蛾,也是第一个计算机程序错误🙂。

调试是什么?有多重要?

调试是什么

调试(英语:Debugging/Debug),又称出错,是发现和减少计算机程序或电子仪器设备中程序错误的一个过程
调试这个词本质上是从硬件这个词来的,比如早期我们说的调试这个机器、调试这个硬件到底能不能工作。后来引申到我们程序里面也是一样的,程序里面也会出现一些问题,这个动作就叫调试。

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

一名优秀的程序员是一名出色的侦探。

每一次调试都是尝试破案的过程。

调试的基本步骤

发现程序错误的存在。
以隔离、消除等方式对错误进行定位。(屏蔽一块代码或者放出一块代码看看会不会出现问题,进而定位到错误产生的区域,然后再解决问题。)
确定错位产生的原因。
提出纠正错误的解决方法。
对程序错误予以改正,重新测试。

debug和release的介绍

在这里插入图片描述

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

当时Debug的时候,我们是可以进行调试的,我们是可以通过调试窗口来观察到的。因为这段代码在编译的过程中产生各种各样的调试信息,它把程序在运行过程的上下文环境的相关信息保留下来。

在这里插入图片描述
当我们改成Release版本时,请看:
在这里插入图片描述
这个时候我们是不可以进行调试的。此时我们按F10进行调试时,它可能是跳着走的,它压根就没有按照我们的逻辑来。一些值不能进行很好的观察。
在这里插入图片描述
Debug文件夹底下放的是编译出来的Debug版本的相关信息,调试版本的可执行程序;而Release文件夹底下放的是Release版本的相关信息,发布版本的可执行程序。
在这里插入图片描述

windows环境调试介绍

快捷键

这是VS2022中的快捷键:
在这里插入图片描述
F5

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

F9

创建断点和取消断点
断点的重要作用,可以在程序的任意位置设置断点
这样就可以使得程序在想要的位置随意停止断点,继而一步步执行下去
那段点有什么用吗?如果说我们有500行代码、5000行代码、甚至是50000行代码让我们进行调试,设置断点可以帮助我们提高调试的效率。
注意断点要打在有意义的地方。

F10

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

F11

逐语句,就是每次都执行一条语句,但是这个快捷键可以使我们的执行逻辑进入函数内部(这是最常用的)。即我们可以看到函数内部的细节。

CTRL+F5

开始执行不调试,如果你想要程序直接运行起来而不进行调试就可以直接使用。即使我们打断点程序也不会停下来

当然VS中还有很多的快捷键,操作起来非常方便,这里就不进行一一列举。

注意:F10和F11处理函数的方式是截然不同的。
断点在多行代码、跨文件使用方面是非常方便的。

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

查看临时变量的值

在调试开始之后,用于观察变量的值。
注意只有按完F10进入调试状态之后才可以。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
注意上图:当数组传参的时候,如果你想在形参的这一部分看到这个数组其实是看不到的,所以我们要添加一个逗号即(arr,12)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
接下来看反汇编:
在这里插入图片描述
在这里插入图片描述
反汇编可以看到我们的C语言代码翻译出来的汇编代码是什么样子的。
接下来看寄存器
在这里插入图片描述
如果我们知道寄存器的名字,我们也可以在监视窗口看到,请看:
在这里插入图片描述
以上是怎么来查看程序执行过程中上下文环境中的变量的值、以及它的内存里的值等等。
下面是调用堆栈:
在这里插入图片描述
用栈的一种这样的形式模拟出来函数的一个调用逻辑,当未来看到函数调用堆栈写的是这样一个逻辑的时候,就能够清楚的看到函数调用的一个逻辑是什么。倘若工程非常大,有几千甚至上万行代码,调试起来逻辑非常复杂的时候,我们可以用调用堆栈来看到当前是怎样的一个调用关系的。
上这些只是简单的调试技巧,需要的是我们多多动手尝试调试,才能有所进步。

调试实例

实例一

实现代码:求1!+2!+3!+…+n!;不考虑溢出。
在这之前我们先来算n!:

//求n!
#include<stdio.h>
int main()
{
	int n = 0;
	int ret = 1;
	int i = 0;
	scanf("%d", &n);
	for (i = 1; i <= n; i++)
	{
		ret *= i;
	}
	printf("%d\n", ret);
	return 0;
}
//需要注意的点是这里的ret进行初始化时一定要初始化为1,而不是0。

在这里插入图片描述
下面就来看一下求1!+2!+3!+…+n!:

#include<stdio.h>
int main()
{
	int n = 0;
	int i = 0;
	int ret = 1;
	int sum = 0;
	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;
}
//这里要注意的点就是每次循环时要重新对ret进行初始化。

在这里插入图片描述
在这里插入图片描述

实例二

#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++)
	{
		printf("hehe\n");
		arr[i] = 0;
	}
	return 0;
}

这段代码会出现死循环的情况:
在这里插入图片描述
在这里插入图片描述
这个地方1在这里插入图片描述
这个地方错误的本质数组的越界导致的。

如何写出好(易于调试)的代码

优秀的代码

1.代码运行正常
2.bug很少
3.效率高
4.可读性高
5.可维护性高
6.注释清晰
7.文档齐全

常见的coding技巧:

1.使用assert
2.尽量使用const
3.养成良好的编程风格
4.添加必要的注释
5.避免编码的陷阱

我们来模拟一下strcpy函数,然后不断优化:

#include<stdio.h>
void my_strcpy(char* dest, char* src)
{
	while (*src != '\0')
	{
		*dest = *src;
		dest++;
		src++;
	}
	*dest = *src;
}
int main()
{
	char arr1[20] = "xxxxxxxxxx";
	char arr2[] = "hello";
	my_strcpy(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

进行优化:

#include<stdio.h>
//void my_strcpy(char* dest, char* src)
//{
//	while (*src != '\0')
//	{
//		*dest++ = *src++;
//		
//	}
//	*dest = *src;//拷贝\0
//}
void my_strcpy(char* dest, char* src)
{
	while (*dest++ = *src++)
	{
		;
	}
}
int main()
{
	char arr1[20] = "xxxxxxxxxx";
	char arr2[] = "hello";
	my_strcpy(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

进行优化:

#include<stdio.h>
#include<assert.h>
void my_strcpy(char* dest, char* src)
{
	/*if (src == NULL || dest == NULL)
	{
		return;
	}*/
	//断言
	//assert中可以放一个表达式,表达式的结果如果为假,就报错,如果为真啥事也不发生
	//assert其实在release版本中优化掉了
	/*assert(src != NULL);
	assert(dest != NULL);*/
	assert(dest && src);
	while (*dest++ = *src++)
	{
		;
	}
}
int main()
{
	char arr1[20] = "xxxxxxxxxx";
	char arr2[] = "hello";
	my_strcpy(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}
//所以未来使用指针之前像判断指针的有效性,我们可以用assert来进行断言
//assert不是仅仅只用来断言指针,一个变量的值,你不想它是什么,我们就可以用assert来进行断言。

进行优化:

#include<stdio.h>
#include<assert.h>
void my_strcpy(char* dest,const char* src)
{
	assert(dest && src);//断言指针的有效性。
	while (*dest++ = *src++)
	{
		;
	}
}
int main()
{
	char arr1[20] = "xxxxxxxxxx";
	char* p = "hello";//p指向的常量字符串是不可以被修改的
	my_strcpy(arr1, p);
	printf("%s\n", arr1);
	return 0;
}
#include<stdio.h>
#include<assert.h>
void my_strcpy(char* dest, const char* src)//这里const修饰的是指针变量本身,但指针所指向的内容,可以通过指针来改变

//int num = 10;
//int* p = &num;
//int n = 1000;
//const修饰指针变量的时候
//1.const放在*的左边,const修饰的是指针所指向的内容,不能通过指针来改变量,不过通过指针来改变了;但是指针变量本身可以修改
// const int* p=&num;
// *p=20//err
// p=&n;//ok
// const int* p=&num;
//2.const放在*的右边,const修饰的指针变量本身,表示指针变量本身的内容不能够被修改,但是指针指向的内容,可以通过指针来修改
//int* const p=&num;
//*p=20;//ok
//p=&n;//err

{
	assert(dest && src);//断言指针的有效性。
	while (*dest++ = *src++)
	{
		;
	}
}
int main()
{
	char arr1[20] = "xxxxxxxxxx";
	char* p = "hello";//p指向的常量字符串是不可以被修改的
	my_strcpy(arr1, p);
	printf("%s\n", arr1);
	return 0;
}

优化:

#include<stdio.h>
#include<assert.h>
char* my_strcpy(char* dest, const char* src)
{
	char* ret = dest;
	assert(dest && src);//断言指针的有效性。
	while (*dest++ = *src++)
	{
		;
	}
	return ret;
}
int main()
{
	char arr1[20] = { 0 };
	char* p = "hello";//p指向的常量字符串是不可以被修改的
	//链式访问
	//当我们把一个函数的返回值作为另外一个参数它就实现了链式访问
	printf("%s\n", my_strcpy(arr1, p));
	return 0;
}

在这里我们考虑了const、考虑了指针的断言、考虑它的返回值类型,同时也包括\0的拷贝

//模拟实现strlen
#include<stdio.h>
size_t my_strlen(const char* str)
{
	size_t count = 0;
	while (*str)
	{
		count++;
		str++;
	}
	return count;
}
int main()
{
	char arr[] = "helloworld";
	printf("%d\n", my_strlen(arr));
	return 0;
}
//我们也可以用指针-指针的方式来实现

编程常见的错误

编译型错误

直接看错误提示信息(双击),解决问题,或者凭借经验就可以搞定。相对来说简单。

链接型错误

看错误提示信息,主要在代码中找到错误信息的标识符,然后定位错误问题所在,一般是标示符名不存在或者拼写错误
在这里插入图片描述

运行时错误

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

在这里插入图片描述
在这里插入图片描述
最后:做一个有心人,积累排错经验。
本文到此也就结束了,感谢各位!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/53111.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于51单片机六车道智能交通灯设计(仿真+源程序+PCB+论文)

资料编号&#xff1a;204 功能介绍&#xff1a;&#xff08;全套毕设资料齐全&#xff09; 本设计的交通灯以十字路口为模型&#xff0c;在实现基本的功能前提下增加了时间及温度的液晶显示。从而还增加了路口高峰期的智能化人工管理机制。 实际生活中交通信号灯的规则千变万…

C++ 大作业/课程设计 小型公司工资管理软件

小型公司工资管理软件一、实验目的二、实验要求三、项目分工四、软件设计0.效果图&#xff1a;1.类的继承关系2.类的设计3.程序运行流程4.异常处理1. 用户输入异常2.文件操作异常五、程序代码一、实验目的 熟悉面向对象程序设计思想&#xff0c;掌握类、类的派生、静态成员、多…

JVM之运行时数据区 方法区

JVM方法区概述设置方法区的大小参数如何解决这些OOM内部结构补充non-final的类变量全局变量&#xff1a;static final常量池运行时常量池方法区的演进StringTable为什么要调整&#xff1f;方法区垃圾回收总览概述 《java虚拟机规范》中明确说明&#xff1a;“尽管所有的方法区…

Tableau可视化项目

文章目录Tableau可视化项目一、数据来源二、项目介绍1.项目目的2.架构三、数据可视化1.人口1.1 总人口、城镇人口、乡村人口1.2 人口出生率、死亡率、自然增长率1.3 人口年龄结构1.4 男女比例2.居民生活水平2.1 医疗情况2.2 恩格尔系数2.3 人均消费支出3.经济3.1 能源弹性系数3…

matlab中图像分割技术之一边缘检测

1.边缘检测 &#xff08;1&#xff09;Roberts边缘算子 (2)Sobel算子 (3)Prewitt算子 (4)拉普拉斯&#xff08;Laplacian&#xff09;算子 (5)LOG&#xff08;Laplacian-Gauss&#xff09;算子 &#xff08;6)坎尼&#xff08;Canny&#xff09;算子 &#xff08;7&#xff09;…

数仓搭建-DWD层

DWD层&#xff08;用户行为日志&#xff09; 6.1.1 日志解析思路 1&#xff09;日志结构回顾 &#xff08;1&#xff09;页面埋点日志 &#xff08;2&#xff09;启动日志 2&#xff09;日志解析思路 6.1.2 get_json_object函数使用 1&#xff09;数据 [{"name&qu…

力扣(LeetCode)128. 最长连续序列(C++)

哈希集合 建立哈希集合&#xff0c;存入所有数。一次遍历数组&#xff0c;对每个数检查是否有前一个数&#xff0c;如果某个数没有前一个数&#xff0c;说明这个数是一个序列的起点&#xff0c;从这个数开始遍历得到序列长度&#xff0c;维护最大序列长度&#xff0c;即是本题…

【SpringBoot项目中Knife4j在线API文档】

目录 1. Knife4j在线API文档基本使用 2. 配置API文档信息 1. Knife4j在线API文档基本使用 Knife4j是一款基于Swagger 2的在线API文档框架。 使用Knife4j的基础步骤&#xff1a; 添加依赖在application.properties / application.yml中添加配置在项目中添加配置类关于依赖项…

甲烷排放通量的计算

甲烷或N2O的排放通量计算公式如下&#xff1a; 式 (1) 中, F为CH4 (以C计) 或N2O (以N计) 排放通量, mgm-2h-1或μgm-2h-1;ρ为标准状况下CH4或N2O的密度, 分别为0.54和1.25 gL-1;V为采样箱体积, m3;A为采样底座内土壤表面积, m2;ΔCΔtΔCΔt表示CH4或N2O的排放速率, μLL-1h-…

浅谈正则表达式——C++正则替换引起的性能下降

目录问题引入正则替换测试常规方法测试模拟外部多次调用正则性能差的原因写在最后问题引入 最近在一次解析大文件(10万行)时&#xff0c;处理空格用到了正则替换&#xff0c;却没想到带来了性能上的问题&#xff0c;特别在此记录给需要的人避坑。假如要用C处理一个字符串首尾的…

4-four: 我收到的赞

我收到的赞 重构点赞功能&#xff08;用上节的功能较为麻烦&#xff0c;需要将用户发布的帖子和评论所获得的赞加起来&#xff09; 以用户为key&#xff0c;记录点赞数量increment(key), decrement(key)。 开发个人主页 以用户为key&#xff0c;查询点赞数量 1.在Redis.Ut…

RabbitMQ------其他知识点(幂等性、优先级队列、惰性队列)(九)

RabbitMQ------其他知识点&#xff08;&#xff09;&#xff08;九&#xff09; 幂等性 用户对于统一操作发起的一次请求或者多次请求的结果是一致的&#xff0c;不会因为多次点击而产生副作用。同一次还款&#xff0c;通过多次点击不会生成多条还款记录&#xff0c;一个人的…

crm客户管理系统为企业带来的价值

简道云CRM场景套件对中小企业来说&#xff0c;crm客户管理系统最核心的作用&#xff0c;一是客户信息(联系信息和洽谈过程&#xff09;的存留&#xff0c;不会因为业务人员的离职造成流失&#xff0c;这点对老板很重要。二是可帮助业务人员&#xff08;客户获取、主管指导等&am…

Win32API操作文件

在Windows编程中CreateFile函数是用得非常多的&#xff0c;不仅可以打开文件&#xff0c;还可以打开管道、邮槽、通信资源、磁盘设备&#xff08;早期Windows)&#xff0c;控制台、目录。该函数返回一个句柄&#xff0c;该句柄可用于根据文件或设备以及指定的标志和属性访问文件…

SpringSecurity(二十)---OAuth2:实现资源服务器(上)资源服务器搭建以及直接调用授权服务器模式

一、 前言 本章将讨论如何使用Spring Security实现一个资源服务器&#xff0c;资源服务器是管理用户资源的组件。另外&#xff0c;学习本章有个前提&#xff0c;需要先把前面搭建授权服务器的相关文章先给阅读&#xff0c;否则可能后面出现的授权服务器相关代码不知道个所以然…

【Redis】缓存击穿的产生情况解决方案

1. 缓存击穿产生 也叫做 热点 Key 问题&#xff0c;高并发访问并且缓存重建业务较复杂的 key 突然失效了&#xff0c;无数的请求想要重建缓存&#xff0c;大量的访问会在瞬间给数据库带来巨大冲击。 2. 解决方案 2.1 方案一&#xff1a;互斥锁 查询缓存不存在时&#xff0c;…

【OpenFOAM】-olaFlow-算例1- baseWaveFlume

算例路径&#xff1a; olaFlow\tutorials\baseWaveFlume 算例描述&#xff1a; 一个基础的二维波浪水槽 算例快照&#xff1a; 图1 波浪模拟结果图2 算例网格文件结构&#xff1a; ├── 0.org │ ├── U │ ├── alpha.water │ ├── alpha.water.org │ └─…

关于Redis的远程连接 Connection: Disconnect on error 问题

bug描述&#xff1a; Connection: Disconnect on error: Connection error: Connection timed outConnection: 192.168.245.128 > connection failed 问题复现&#xff1a; redis版本&#xff1a; redis-6.2.6 Linux版本&#xff1a;CenterOS 7 在linux上已经完成了red…

第五章. 可视化数据分析分析图表—常用图表的绘制1—折线图,柱形图

第五章. 可视化数据分析分析图 5.3 常用图表的绘制1—折线图,柱形图 本节主要介绍常用图表的绘制&#xff0c;主要包括折线图&#xff0c;柱形图。 1.折线图&#xff08;matplotlib.pyplot.plot&#xff09; 折线图可以显示随时间而变化的连续数据&#xff0c;适用于显示在相…

Hadoop原理与技术——hdfs命令行基本操作

一、实验目的 熟悉hdfs命令行基本操作 二、实验环境 Windows 10 VMware Workstation Pro虚拟机 Hadoop环境 Jdk1.8 三、实验内容 1&#xff1a;hdfs常见命令&#xff1a; &#xff08;1&#xff09;查看帮助&#xff1a;hdfs dfs -help &#xff08;2&#xff09;查看当前目录…