#15VS调试技巧#

1.BUG

第一次被发现的导致计算机错误的飞蛾,也是第一个计算机程序错误

2.调试

(1)定义

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

(2)分为几个步骤:

发现程序错误的存在

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

确定错误产生的原因

提出纠正错误的解决办法

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

(3)Debug和Release的介绍

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

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

#include <stdio.h>
int main() 
{
	int i = 0;
	int arr[10] = {0};
	for(i=0; i<=12; i++) 
	{
		arr[i] = 0;
		printf("hehe\n");
	}
	return 0;
}

Debug死循环

Release会优化 不会死循环

后文会说明死循环原因

如果遇到

DEBUG  没问题
Release 可能有问题 优化过了

记录下来

3.Windows环境调试介绍

(1)调试环境的准备

在环境中选择 Debug 选项,才能使代码正常调试。

(2)快捷键

1°F5

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

2°F9

创建断点和取消断点

断点的重要作用,可以在程序的任意位置设置断点

这样就可以使得程序在想要的位置随意停止执行,继而一步步执行下去。

F5+F9配合

F9先设置断点

F5直接跳到断点处

右击断点可以设置条件(比如循环中可以直接让i等于一个值)

#include<stdio.h>
int main()
{
	int i = 0;
	for (i = 0; i < 100; i++)
	{
		printf("%d ", i);
	}
	for (i = 0; i < 100; i++)
	{
		printf("%d ", 10 - i);
	}
	return 0;
}

可以F5和F9配合

3°F10

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

4°F11

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

#include<stdio.h>
int Add(int x, int y)
{
	return x + y;
}
int main()
{
	printf("hehe\n");
	int a = 20;
	int b = 10;
	int c = Add(a, b);
	return 0;
}

F10 会放过Add函数
F11 每条语句都会走

5°CTRL + F5

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

6°SHIFT+F5

停止调试

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

调试的窗口中

1°查看临时变量的值

2°查看内存信息

3°查看调用堆栈  通过调用堆栈,可以清晰的反应函数的调用关系以及当前调用所处的位置。

4°查看汇编信息  点击反汇编 可以切换到汇编代码

5°查看寄存器信息  可以查看当前运行环境的寄存器的使用信息。

4.调试实例

#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;
		for (j = 1; j <= i; j++)//如何快速调试到3的阶乘 
		{//断点右键可以设置条件 然后加上F5直接快速调试到3的阶乘
			ret *= j;
		}
		//ret没有从1开始算阶乘
		sum += ret;
	}
	printf("%d\n", sum);
	return 0;
}

这时候我们如果3,期待输出9,但实际输出的是15。

在循环中用断点条件调试

发现ret没有从1开始算阶乘

在sum+=ret后面加上一句ret=1就可以解决问题

#include <stdio.h>
int main()
{
    int i = 0;
    int arr[10] = {0};
    for(i=0; i<=12; i++)
    {
        arr[i] = 0;
        printf("hehe\n");
    }
    return 0;
}

为什么死循环?

当你调试的时候 用监视窗口 发现arr[12]和i的地址相同
所以当把arr[12]赋值为0的时候 i也等于0了 所以死循环

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

(1)优秀的代码和技巧

优秀的代码

1. 代码运行正常

2. bug很少

3. 效率高

4. 可读性高

5. 可维护性高

6. 注释清晰

7. 文档齐全

技巧

1. 使用assert

2. 尽量使用const

3. 养成良好的编码风格

4. 添加必要的注释

5. 避免编码的陷阱。

(2)my_strcpy的一步一步优化

#include <stdio.h>
void my_strcpy(char* dest, char* src)
{
	while (*src != '\0')
	{
		*dest = *src;
		src++;
		dest++;
	}
	*dest = *src;// \0拷贝
	//6分
}
int main()
{
	//strcpy
	//字符串拷贝
	char arr1[] = "#########";
	char arr2[] = "bit";
	my_strcpy(arr1,arr2);// \0会被拷贝过去
	printf("%s\n", arr1);
	return 0;
}

6分 while内部可以优化

#include <stdio.h>
void my_strcpy(char* dest, char* src)
{
	while (*dest++ = *src++)
	{
		;
	}
	//7分
}
int main()
{
	//strcpy
	//字符串拷贝
	char arr1[] = "#########";
	char arr2[] = "bit";
	my_strcpy(arr1,arr2);// \0会被拷贝过去
	printf("%s\n", arr1);
	return 0;
}

7分 如果传过来空指针的话 函数内部没有干任何事

#include <stdio.h>
#include <assert.h>
void my_strcpy(char* dest, char* src)
{
	assert(dest!=NULL);//断言 引入头文件
	assert(src != NULL);//编译的时候直接告诉你问题
	while (*dest++ = *src++)
	{
		;
	}
	//8分 
}
int main()
{
	//strcpy
	//字符串拷贝
	char arr1[] = "#########";
	char arr2[] = "bit";
	my_strcpy(arr1,arr2);// \0会被拷贝过去
	printf("%s\n", arr1);
	return 0;
}

8分 assert断言解决空指针问题 但我们希望我们要复制的(源头的)字符串不要改变 加const

#include <stdio.h>
#include <assert.h>
void my_strcpy(char* dest,const char* src)
//加const 提前暴露错误 不会被dest赋值
{
	assert(dest != NULL);
	assert(src != NULL);
	while (*dest=*src)//*src++ = *dest++这样会报错
	{
		;
	}
	//这样的话只能调试找 但是加了const说明源头不能改变
	//可以直接暴露错误 代码更加安全 
	//9分
}
int main()
{
	//strcpy
	//字符串拷贝
	char arr1[] = "#########";
	char arr2[] = "bit";
	my_strcpy(arr1,arr2);// \0会被拷贝过去
	printf("%s\n", arr1);
	return 0;
}

9分 加了const后代码更加安全 这时函数没有返回

#include <stdio.h>
#include <assert.h>
char* my_strcpy(char* dest,const char* src)
//拷贝之后返回目的地的地址
{
	char* ret = dest;
	assert(dest != NULL);
	assert(src != NULL);
	//把src指向的字符串拷贝到dest指向的空间
	//包含'\0'字符
	while (*dest++=*src++)
	{
		;
	}
	return ret;
	//10分
}

int main()
{
	//strcpy
	//字符串拷贝
	char arr1[] = "#########";
	char arr2[] = "bit";
	//my_strcpy(arr1,arr2);// \0会被拷贝过去
	//printf("%s\n", arr1);
	//10分可以改成
	printf("%s\n", my_strcpy(arr1, arr2));
	return 0;
}
 

10分 把src指向的字符串拷贝到dest指向的空间 拷贝之后返回目的地的地址 优化主函数内部打印部分

总结:

1°assert const多用

2°想想如何简化语句

3°添加一些注释

(3)const的使用

#include <stdio.h>
int main()
{
	const int num = 10;
	int* p = &num;
	//const int* p=&num;加const 即使把num给到p也无法修改
	//const放在指针变量的*左边时 修饰的*p 也就是说
	//不能通过p来改变*p(num)的值 *p无法改变
	//const放在指针变量的*右边时 修饰的是指针变量p本身
	//也就是说p不能被改变了 p得到的地址无法改变
	*p = 20;
	printf("%d\n", num);
	//p破坏了规则 会改变num
	return 0;
}

const修饰指针变量的时候:

1. const如果放在*的左边,修饰的是指针指向的内容保证指针指向的内容不能通过指针来改变。但是指针变量本身的内容可变。

2. const如果放在*的右边,修饰的是指针变量本身保证了指针变量的内容不能修改,但是指针指向的内容,可以通过指针改变。

my_strlen的实现

#include<stdio.h>
#include<assert.h>
int my_strlen(const char* str)
{
	int count = 0;
	assert(str != NULL);
	while (*str != '\0')
	{
		count++;
		str++;
	}
	return count;
}

int main()
{
	char arr[] = "abcdef";
	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}

6.编程常见的错误

1°编译型错误

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

2°链接型错误

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

3°运行时错误

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

#15VS调试技巧#完

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力的小恒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值