VS中实用的调试技巧,你不来看一看吗?

调试技巧


本篇内容:

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

1.什么是Bug?

Bug翻译过来是臭虫的意思,如今简单来讲就是程序错误
注释:第一次被发现的导致计算机错误的飞蛾,也是第一个计算机程序错误

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

调试就是一次推理的过程,一次查错的过程,一名优秀的程序员是一名出色的侦探。

这可能是我们写代码的样子:
在这里插入图片描述
在这里插入图片描述

1.那么说到底什么是调试呢?

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

2.调试的基本步骤:

在这里插入图片描述

3.debug和release介绍

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

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

首先,我们写一段代码,然后看看这段代码在两个不同环境下的区别

#include<stdio.h>
int main()
{
	int n = 0;
	int a = 0;
	scanf("%d %d", &n, &a);
	int i = 0;
	int k = 0;
	int sum = 0;
	for (i = 0; i < n; i++)
	{
		k = k * 10 + a;
		sum += k;
	}
	printf("%d\n", sum);
	return 0;
}

在这里插入图片描述

3.1Debug环境下

  • 按F10进入调试,在调试中点击窗口,在窗口中点击监视

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

3.2Release环境下

  • 按F10进入调试,在调试中点击窗口,在窗口中点击监视

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

3.3总结

  • 同一段代码在Debug环境下,包含调试信息,可进行调试,代码大小比Release环境下大,运行速度相对Release环境下慢。
  • 在Release环境下,不包含调试信息,不可进行调试,代码大小比Debug环境下小,运行速度更快,同时也进行了优化。
  • 作为程序员我们用的最多的还是Debug版本

4.windows环境调试介绍

注意:windows环境下我们用的是vs,Linux环境下编译器是Gcc,调试器是Gdb。

4.1调试环境的准备

在这里插入图片描述

4.2快捷键的使用

最常使用的几个快捷键:
1.F5

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

2.F9

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

一般而言,F5和F9是一起使用的。
3.F10

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

4.F11

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

一般逐语句的话,F10和F11没什么区别,但想要进入函数内部必须用F11。
5.CTRL + F5

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

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

5.1 查看临时变量的值

按F10,在调试开始之后,用于观察变量的值

先看一段简单的代码,如何观察a,b的值?

int add(int x, int y)
{
	return x+y;
}
int main()
{
	int a = 0;
	int b = 0;
	scanf("%d %d", &a, &b);
	int ret=add(a, b);
	printf("%d\n", ret);
	return 0;
}

一定记得先按F10,然后在调试中点击窗口,在窗口中点击监视
在这里插入图片描述
输入监视的变量:
在这里插入图片描述
删除某个变量按delete。


接下来看看数组如何监视?
F10调试后,调试—窗口—监视
在这里插入图片描述
进入函数呢?
在这里插入图片描述
如何完整看到数组里的元素呢?
在这里插入图片描述

5.2查看内存信息

先按F10进行调试,调试—窗口–内存

int a = 0;
int main()
{
	int b = 20;
	int arr[12] = { 1,2,3,4,5,6,7,8,9 };
	return 0;
}

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

5.3查看寄存器和反汇编

按F10进入调试后,调试—窗口—反汇编/寄存器
在这里插入图片描述

5.4查看调用堆栈

首先,大概了解一下队列和栈
在这里插入图片描述
在这里插入图片描述
注意:局部变量放在栈区,一般来说,栈区的使用习惯是:先使用高地址,再使用低地址。
看一段代码

void test2()
{
	printf("hehe\n");
}
void test1()
{
	test2();
}
void test()
{
	test1();
}
int main()
{
	test();
	return 0;
}

按F10进入调试后,调试—窗口—调用堆栈
在这里插入图片描述
在这里插入图片描述

6.一些调试的实例

实现代码:求 1!+2!+3! …+ n! ;

int main()
{
 int i = 0;
 int sum = 0;
 int n = 0;
 int ret = 1;
 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;
}

输入:
3

运行结果:
15

为什么?
大家可以自己动手试试,实践才能出真知。
原因:ret每次都要赋值为1,但此程序没要。

7.如何写出好的代码?

优秀代码:

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

常见的coding技巧:

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

首先,讲一下assert的用法:
assert是断言语句,assert包含表达式,如果表达式为真就啥事都不做,如果表达式为假就会报警告,当然在使用assert时要有头文件#include<assert.h>。

趁热打铁,我们来模拟strcpy函数

strcpy(目标字符串,源头字符串);
在这里插入图片描述

未优化的代码

void my_strcpy(char* dest, char* str)
{
	while (*str != '\0')
	{
		//拷贝内容
		*dest++ = *str++ ;
	}
	//拷贝'\0'
	*dest = *str;
}
int main()
{
	char arr1[20] = "***********";
	char arr2[] = "hello";
	my_strcpy(arr1, arr2);
	printf("%s\n",arr1);
	return 0;
}

运行结果:
hello


优化后的代码

#include<assert.h>
char* my_strcpy(char* dest,const  char* str)
{
	char *ret=dest;
	assert(str && dest);//断言语句,用来判断是否为空指针
	while (*dest++ = *str++)
	{
		;
	}
	return ret;//在cplusplus上我们可以看到库函数strcpy是有返回值的,返回值是目标字符串的首地址
}
int main()
{
	char arr1[20] = "***********";
	char arr2[] = "hello";
	printf("%s\n",my_strcpy(arr1, arr2));
	return 0;
}

7.1const的作用

const修饰变量,本身是变量具有常值属性,即不可直接修改

int main()
{
	const int num = 10;
	num = 20;//不可修改
	return 0;
}

运行结果:
编译器报错


但是,我们可以间接修改,用指针修改试试

int main()
{
	const int num = 10;
	int* p = &num;
	*p = 20;
	printf("%d\n", num);
	return 0;
}

运行结果:
20


那么该如何避免这种漏洞呢?

const修饰指针时
1.const放在* 号左边时,指针变量是可以改的,但是指向内容是不可改的,除非二级指针。
2.const放在* 号右边时,指针变量是不能改的,但是指向内容是可以改的。

第一种,看一段代码:

	int num = 10;
	const int *  p = &num;
	p=0x0000ff48;
	printf("%p",p);

运行结果:
0000FF48

int main()
{
	int num = 10;
	const int *  p = &num;
	*p = 20;//不可修改,除非二级指针
	printf("%d\n", num);
	return 0;
}

运行结果:
编译器报错


第二种,看一段代码:

int main()
{
	int num = 10;
	int *const p = &num;
	*p = 20;
	printf("%d\n", num);
	return 0;
}

运行结果:
20

	int num = 10;
	int *const p = &num;
	p = 0x0000ff44;
	printf("%p", p);

运行结果:
编译器报错

补:unsigned int可以写成size_t

8.编程常见的错误

8.1编译型错误

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

8.2链接型错误

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

解决方法:按Ctrl+f查找,定位,修改。

在这里插入图片描述

8.3运行时错误

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

总结

以上就是调试的所有内容了,调试还是得多练,如果喜欢本篇,不妨点个小小的赞,如果想要了解更多编程知识,不妨点个关注,你们的关注是对我最大的支持,谢谢可爱的你的喜欢,下期间!!!

  • 13
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值