【C语言-调试技巧】爷傲奈我何?bug来治你

前言

调试对程序员来说是必不可少的,没人能保证一辈子写的代码一个bug都没有。所以学会调试,很重要。

没有手绘封面了,apple pencil丢了。。。


1. 调试

1.1 调试是什么?

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

1.2 怎么调试?

  • 发现错误
  • 通过 隔离 、消除 等方式,定位错误
  • 确定错误原因
  • 思考解决办法
  • 实现解决办法,对程序除错
  • 重新测试

1.3 Debug 和 Release

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


2. Windows下的调试

2.1 调试环境

环境选项中选 Debug

2.2 调试快捷键

F5
启动调试
常用于跳到下一个断点

F9
创建/取消断点
让程序在你希望的地方(至少是有意义的代码处)停下来

F10
逐过程
过程:一次函数调用,一条语句,都是一个过程

F11
逐语句
一次只执行一条语句,常用于进入函数内部

Ctrl + F5
不调试,直接执行程序

2.3 调试的重要部分:“监视”

开始调试后,在“调试” – “窗口” 里,可以任意找到想监视的信息

在这里插入图片描述


3. 调试实例

3.1 例1

#include<stdio.h>
int main()
{
	int i = 0;
	int n = 0;
	int sum = 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;
结果:15
 3! + 2! + 1= 6+2+1 = 9
 为什么得到15}

对 ret 的需求是每次存储 n 的阶乘,但是调试发现,ret 的值一直累加,正确代码应该是这样:

#include<stdio.h>
int main()
{
	int i = 0;
	int n = 0;
	int sum = 0;
	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;
}

调试过程中,时刻观察程序走向是不是和脑中构思好的一致

3.2 例2

#include<stdio.h>
int main()
{
	int arr[10] = { 0 };
	int i = 0;
	for (i = 0; i <= 12; i++)
	{
		arr[i] = 0;
		printf("hehe\n");
	}
	return 0;
}
结果:死循环/报错(越界访问)

是的,这里越界访问了,但是根据 vs 的内存使用规则,越界访问的 arr[12] 刚好是 i ,每当程序要结束 , i 就又被赋成 0 ,导致死循环
在这里插入图片描述

vs在栈区使用空间,总是从高地址开始使用
vs数组的使用中,随着下标增加,地址由低到高

*静态区是右下角的方格,不知道怎么跑上去了 -_-


4. 如何写出好,还易于调试的代码?

4.1 优秀的代码

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

4.2 如何向优秀代码靠近?

  1. 使用assert(断言,我视它为保安)
  2. 尽量使用const
  3. 良好代码风格(适合的缩进,空格之类)
  4. 添加必要的注释(明了简洁)
  5. 避免代码的陷阱

4.3 优秀代码示例(多向优秀代码学习)

#include<stdio.h>
#include<assert.h>
//************
//= char* my_strcpy( dst , src ) - Copy one string over another
// 
//= Purpose:
//			Copies the string "src" into the spot
// 			specified  by dest ; assumes enough room.
// 
//= Entry:
//			char* dst - string over which "src" is to be copied 
//			const char* src - string to be copied over "dst"
//
// = Exit:
//			The address of "dst"
//**************

char* my_strcpy(char* dst, const char* src)
{
	char* tool = dst;
	//两个形参都不为NULL
	assert(dst && src);

	while (*tool++ = *src++)// \0 = 0
	{
		;//copy src over dst
	}

	return dst;
}
int main()
{
	char str1[] = "Coding is funny!";
	char str2[30] = { 0 };
	printf("%s\n", my_strcpy(str2, str1));
	return 0;
}

4.4 const 的作用

const:赋予常量属性

看看例子(参考前辈的例子)

#include <stdio.h>
//代码1
void test1()
{
    int n = 10;
    int m = 20;
    int *p = &n;
    *p = 20;//ok?
    p = &m; //ok?
}
void test2()
{
     //代码2
    int n = 10;
    int m = 20;
    const int* p = &n;
    *p = 20;//ok?
    p = &m; //ok?
}
void test3()
{
    int n = 10;
    int m = 20;
    int *const p = &n;
    *p = 20; //ok?
    p = &m;  //ok?
}
int main()
{
    //测试无cosnt的
   test1();
    //测试const放在*的左边
    test2();
    //测试const放在*的右边
    test3();
    return 0; }

总结:就是看 const 在谁的左边

  1. 在 * 左边,就是修饰指针变量指向的内容:
    指针指向的值被赋予常量属性
  2. 在 * 右边,就是修饰指针变量:
    指针变量里的地址被赋予常量属性

5. 编程常见错误

5.1 编译型错误

多是语法问题,双击错误信息,根据错误信息修正即可

5.2 链接型错误

多是 标识符不存在拼写错误没有声明

5.3 运行时错误

逐步调试,细心对照:“是否和脑中构思的相同?”

5.4 通向 “无bug” 的捷径

不断积累排错经验,两点之间线段最短,脚踏实地就是最快的捷径


今天的分享就到这里


培根的blog,不断进步
:粉丝、阅读量不是初衷,写博客是为了倒逼自己输出,提升自己,不浮躁,不虚荣

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

周杰偷奶茶

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

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

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

打赏作者

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

抵扣说明:

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

余额充值