调试技巧总结

2023-05-16

目录

1.调试是什么?

2. 调试的基本步骤

3.Debug和Release的介绍 

4. 调试实例

4.1 实现代码:求 1!+2!+3! ...+ n! ;不考虑溢出

4.2 分析这段代码

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

5.1 优秀的代码:

5.2 示范

6.补充知识

6.1 const讲解 

7.编译常见的错误


1.调试是什么?

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

2. 调试的基本步骤

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

3.Debug和Release的介绍 

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

4. 调试实例

4.1 实现代码:求 1!+2!+3! ...+ n! ;不考虑溢出

#include <stdio.h>

int main()
{
	int n = 0;
	scanf("%d", &n);
	int ret = 1;
	int sum = 0;
	for (n = 1; n <= 3; n++)
	{
		int i = 0;
		for (i = 1; i <= n; i++)
		{
			ret *= i;
		}
		sum += ret;
	}
	printf("%d\n", sum);
	return 0;
}

1!+2!+3!= 9 但上述代码算出的结果是15.这说明我们的代码出了问题,开始进行调试。 

当我们进行计算3的阶乘的时候,ret的值还是2,这种代码的写法是从1一直乘到所求的那个数上去,所以我们要将每次进入循环的ret的值设为1。

正确代码

int main()
{
	int n = 0;
	scanf("%d", &n);

	int ret = 1;
	int i = 0;
	int sum = 0;
	for (i = 1; i <= n; i++)
	{
		ret *= i;
		sum += ret;
	}
	printf("%d", sum);
	return 0;
}

4.2 分析这段代码

#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;
}

首先,我们很容易就看出数组越界访问了,但在组代码的结果在不同环境下的结果是不一样的。在vs中,它是会死循环的。

解释:因为内存中栈区的使用习惯是先使用高地址空间,再使用低地址空间,于是乎操作系统就在栈中就先分配给了局部变量i一块空间,随后又给数组分配了一块空间,而内存中数组是连续存放的,它随着下标的增长地址由低到高。而我们在数组中放了元素0之后,在vs中数组越界了,vs这个编译器依然会给数组越界后的内存放上数字0,但是vs中变量和数组在内存中的位置相差两个字节,当程序到arr[12]的时候,实际上是访问到了变量i的空间,此时将arr[12]中放上数据0,实际上就是将变量 i 中的值修改为了0,因为此时两者指向的是同一块内存空间。

再次注意,不同的编译环境中,结果可能是不同的。在vc6.0中 i 和 arr 在内存中是没有空隙的。

而在gcc中,两者之间相差一个字节,在vs中则相差两个字节。

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

5.1 优秀的代码:

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


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

5.2 示范

模拟实现strcpy,博主将展示不断优化的代码

博主将展示不断优化的代码,满分为10分

方法一,5分 

void my_strcpy(char* dest, char* src)//传进来两个指针
{
	while (*src != '\0')
	{
		*dest = *src;
		src++;
		dest++;
	}
	*dest = *src;   //这里最后将src指向的'\0'赋给dest指向的区域
}

int main()
{
	char arr1[] = "hello haha";
	char arr2[20] = "xxxxxxxxxxxxxx";

	my_strcpy(arr2,arr1);
	printf("%s\n", arr2);
	return 0;
}

 方法二,6分

void my_strcpy(char* dest, char* src)//传进来两个指针
{
	while (*src != '\0')
	{
		*dest++ = *src++;
		
	}
	*dest = *src;   //这里最后将src指向的'\0'赋给dest指向的区域
}

int main()
{
	char arr1[] = "hello haha";
	char arr2[20] = "xxxxxxxxxxxxxx";

	my_strcpy(arr2,arr1);
	printf("%s\n", arr2);
	return 0;
}

 方法三,7分

void my_strcpy(char* dest, char* src)//传进来两个指针
{
	while (*dest++ = *src++)
	{
		;
	}  
}

int main()
{
	char arr1[] = "hello haha";
	char arr2[20] = "xxxxxxxxxxxxxx";

	my_strcpy(arr2, arr1);
	printf("%s\n", arr2);
	return 0;
}

 解释一下,这句语句,其实就是先将*src赋给*dest,然后src++,dest++,当src指向‘\0’的时候,先将'\0'赋给dest,但是此时*dest++ = *src++ 这个表达式的值为0,循环就结束了。

方法四,8分 

#include <stdio.h>
#include <assert.h>

void my_strcpy(char* dest, char* src)
{
	assret(src != NULL);
	assret(dest != NULL);
	while (*dest++ = *src++)
	{
		;
	}  
}

int main()
{
	char arr1[] = "hello haha";
	char arr2[20] = "xxxxxxxxxxxxxx";
	int* p = NULL;

	my_strcpy(arr2, arr1);
	printf("%s\n", arr2);
	return 0;
}

这里,用到了一个函数assert(断言),这样做的原因是我们在传参中,有时候会误将空指针给传进去,这时候就会出现读写异常,而使用assert之后呢,可以快速的帮我们定位到问题的所在,因为当你传了空指针时,连编译都不让你编过去。

方法5, 9分

void my_strcpy(char* dest, char* src)
{
	assret(src && dest);
	
	while (*dest++ = *src++)
	{
		;
	}
}

int main()
{
	char arr1[] = "hello haha";
	char arr2[20] = "xxxxxxxxxxxxxx";
	//int* p = NULL;

	my_strcpy(arr2, arr1);
	printf("%s\n", arr2);
	return 0;
}

这里就是将两个assert组合了起来。

方法6,满分

不知道你有没有注意到strcpy返回的是一个字符型的指针,还有src前面有一个const修饰。我们要模拟就要做到最好。 my_strcpy函数设计返回值类型是为了实现函数的链式访问。

#include <stdio.h>
#include <assert.h>

char* my_strcpy(char* dest, const char* src)
{
	assert(src && dest);
	char* ret = dest;
	while (*dest++ = *src++)
	{
		;
	}
	return ret;
}

int main()
{
	char arr1[] = "hello haha";
	char arr2[20] = "xxxxxxxxxxxxxx";
	//int* p = NULL;

	
	printf("%s\n", my_strcpy(arr2, arr1));
	return 0;
}

6.补充知识

6.1 const讲解 

int main()
{
	//const int num = 10;  //这里编译不同的,是因为表达式必须是可修改的值
	//num = 20;            //这里的num此时已经是常变量了,所以不可被修改。
	
	int num = 20;
	int n = 0;
	const int* p1 = &num;
	p1 = &n;

	int* const p2 = &num;
	*p2 = 10;

	return 0;
}

const 可以修饰指针
const 放在*的左边(const int* p1;)
const修饰的是*p1,表示p1指向的对象不能通过p1来改变,但是p1变量中的地址是可以改变的
const 放在*的右边(int* const p2;)
const 修饰的是p2,表示p2的内容不能被改变,但是p2指向的的对象是可以通过p来改变的

7.编译常见的错误


编译型错误就是语法错误
链接型错误
运行时错误 - 借助调试解决的错误

#include <stdio.h>

int Add(int x, int y)
{
	return x + y;
}

int main()
{
	int a = 10;
	int b = 20;
	
	int c = Add(a, b);
	
	printf("%d\n", c);

	return 0;
}

void* p;

void test(void)
{
	printf("hehe\n");
}

int main()
{

	return 0;
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

调试技巧总结 的相关文章

  • C++静态成员静态函数,语法+示例,超详细!!

    类的静态成员定义 静态成员是指用static定义的变量 就是成员变量前面加了个 static 静态变量分为全局变量和局部变量两种 静态全局变量 静态全局变量声明在程序所有函数之外 xff0c 其作用域与全局变量一样 xff0c 所有函数体都
  • 数据结构。顺序栈的一些操作(两种定义、进栈、出栈、获取栈顶、共享栈)

    栈的两种定义 define MAXSIZE 50 静态 define ERROR 0 typedef struct int data MAXSIZE int top SqStack void init Stack SqStack amp s
  • 【无标题】数据结构。链栈的一些操作(定义、进栈、出栈)

    include lt iotream gt include lt stdio h gt include lt stdlib h gt using namespace std 构造节点 typedef struct StackNode int
  • 数据结构。栈+队列判断字符序列是否属于回文数字或回文字符,非常详细

    精髓 xff0c 请仔细体会 bool test SqQueue amp Q Stack amp s char c while c 61 getchar 61 39 64 39 因为栈是先进后出 push Stack s c 队列是先进先出
  • 动画函数添加回调函数

    回调函数原理 xff1a 函数可以作为一个参数 将这个函数作为参数传到另一个函数里面 xff0c 当那个函数执行完之后 xff0c 再去执行传进去的这个函数 xff0c 这个过程就叫做回调 回调函数的位置 xff1a 写到定时器结束的位置
  • 数组向后移动M位(C语言)

    include lt stdio h gt int main int N M int a 100 scanf 34 d d 34 amp N amp M for int i 61 0 i lt N i 43 43 scanf 34 d 34
  • 数据结构——顺序表

    一 定义 顺序表是一种线性的存储结构 xff0c 采用一段连续的地址存储单元依次存放数据元素 xff0c 一般采用数组存储 顺序表一般可分为 xff1a 1 静态顺序表 xff1a 使用定长数组存储元素 2 动态顺序表 xff1a 使用动态
  • WSL2安装

    目录 什么是WSL2 xff1f 安装WSL导入镜像设置Linux用户信息如何在资源管理器查看文件 xff1f 参考链接 笔者使用环境 Windows11 22H28GB RAM512GB ROM 什么是WSL2 xff1f WSL2是Wi
  • 计分板-2021安徽省机器大赛程序设计赛道

    题目描述 Alice 和 Bob 在玩游戏 xff0c 两个人分别有一个计分板 xff0c 记录各自的得分 得分 X 的 字典序严格小于得分 Y xff0c 那么就认为得分 X 高于得分 Y Bob 想要自己的分数高 于 Alice xff
  • 九、Debian 10 SSH

    要求 1 安装 SSH 工作端口监听在 19210 2 仅允许 InsideCli 客户端进行 ssh 访问 其余所有主机的请求都应该拒绝 3 在 cskadmin 用户环境 InsideCli 下可以使用密钥免密码登录 并且拥有超级 管理
  • Oracle函数中常用的日期函数实用案例

    获取系统当前时间 select sysdate from dual select current date from dual select localtimestamp from dual 获取两天以后的时间 select sysdate
  • 十六、Debian 10 WEB服务(lighttpd)

    题目要求 1 安装 lighttpd 使用其他 web 平台 以下功能均不得分 2 启用 fastcgi php 模块 3 index php 网页内容显示当前服务器的日期和时间 刷新页面时间自动更 新 解题步骤 1 了解 lighttpd
  • 数组及字符处理(C语言复习)

    1 编写程序 从键盘上输入10个整数 xff0c 求其中最大值和最小值及其序号 例 xff1a 输入 xff1a 88 95 10 3 6 81 12 77 166 35 输出 xff1a 最大值 xff1a 166 xff0c 序号 xf
  • 如何用python获取单个文件 或 文件夹中所有文件的行数

    目录 一 获取单个文件的行数二 获取文件夹中所有文件的行数三 关于os walk 函数 一 获取单个文件的行数 本例展示获取单个txt文件中的行数 xff1a span class token comment 统计单个文件的行数 span
  • 保姆级教程,阿里云快速搭建个人网站

    首先想要搭建一个网站需要一个域名和服务器 xff0c 我们先去阿里云搜索这两个东西 xff0c 然后分别去购买一下 服务器这里有轻量级应用服务器和云服务器ECS都可以选择 我选择的是ECS xff0c 然后我们去购买 xff0c 产品区域选
  • C语言-实现栈的基本操作(顺序栈)

    下面用两种方式来构建顺序栈 xff0c 分别是将top定义为指针类型和将top定义成指针下标两种形式 xff0c 实现栈的基本操作 目录 方法一 xff1a 1 1结构定义 1 2 完整代码 1 3测试用代码 xff08 用来逐步测试以上栈
  • 电脑无法打开相机照片怎么解决?

    相机拍照后的照片 xff0c 大部分人把照片保存在电脑上 xff0c 这样就可以把相机的内存卡腾空出来进行新的一轮拍摄 最近有新朋友询问如果电脑上的照片打不开怎么办 xff1f 首先我们要了解什么情况下电脑的照片会打不开 xff0c 原因可
  • Ubuntu22.04网络连接不上的问题

    平台 xff1a virtualbox Ubuntu22 04 在VirtualBox虚拟机上Ubuntu莫名其妙的连不上网 xff0c 在网络搜寻并尝试各种解答后问题终于得以解决 网络连接启动未打开 xff1b 在设置里面应该将网络勾选
  • 如何在Linux中安装redis(图文教程,按照步骤可安装成功)

    目录 1 在Redis版本库 xff1a https download redis io releases 可根据自己的需求选择下载对应的版本 xff0c 然后直接下载 2 通过Xftp工具进行上传 xff0c 选择指定的应用拖到右侧对应的

随机推荐

  • C++11入门

    文章目录 1 C 43 43 11简介2 列表初始化2 1 initializer list2 2 小结 3 声明3 1 auto3 2 decltype3 3 nullptr 4 范围for4 1 使用4 2 使用条件 5 STL新容器5
  • 51单片机实例6——用定时器T0中断控制1位LED秒闪烁

    用定时器T0中断控制1位LED秒闪烁 1 设计目的 用定时器T0中断控制1位LED秒闪烁 2 仿真电路 3 程序设计 xff08 C语言 xff09 include lt reg51 h gt include lt math h gt sb
  • ubuntu 18.04 ARM架构ECS更换默认源(2020.04)

    这里写自定义目录标题 0x00 ubuntu18 04 apt国内源0x01 一个source list的构成0x02 更换并更新源0x03 其他 0x00 ubuntu18 04 apt国内源 最近开的新的arm架构的ECS更换国内源的记
  • 【python】使用pip安装python第三方库(简单易懂)

    作者 二月知野 专栏 人生苦短 我学python Python语言有超过12万个第三方库 xff0c 覆盖信息技术几乎所有领域 例如 网络爬虫 自动化 数据分析与可视化 WEB开发 机器学习和其他常用的一些第三方库 什么是pip pip是p
  • PTA 7-1 字符串模式匹配(KMP)

    给定一个字符串 text 和一个模式串 pattern xff0c 求 pattern 在text 中的出现次数 text 和 pattern 中的字符均为英语大写字母或小写字母 text中不同位置出现的pattern 可重叠 输入格式 输
  • 洛谷P1233 木棍加工 动态规划 最大上升子序列

    P1233 木棍加工 Java 实现 思路 xff1a 这题的思路一定是贪心 xff0b 动态规划 xff0c 当遇上既有长度又有宽度的木棒的 xff0c 可以先对长度进行排序 xff08 如果长度相同 xff0c 则根据宽度排序 xff0
  • 解决selenium使用webdriver.Chrome()报错的问题

    运行时报错 第一个解决方法 xff1a driver 61 webdriver Chrome 34 webdriver驱动路径 34 记得是绝对路径 xff0c 记得和谷歌浏览器放在一起 谷歌驱动下载 xff08 你安装驱动才可以用seln
  • 猜数字游戏(c语言实现)

    一个简单的猜数字游戏送给大家 xff0c 非常适合初学者练习 xff0c 为此 xff0c 我将详细地讲解每一个步骤 我的码云地址 xff1a https gitee com small protrusion c practice code
  • goto语句实现关机小程序

    C语言中提供了可以随意滥用的 goto语句和标记跳转的标号 从理论上 goto语句是没有必要的 xff0c 实践中没有goto语句也可以很容易的写出代码 而goto语句无非就是直接跳到符号那里去 xff0c 这个符号不固定 xff0c 可以
  • C语言中的函数(详解)

    目录 1 函数是什么 2 c语言中函数的分类 xff1a 2 1 库函数 2 自定义函数 3 函数的参数 3 1 实际参数 xff08 实参 xff09 3 2 形式参数 xff08 形参 xff09 4 函数的调用 xff1a 4 1 传
  • C语言练习题(递归)

    目录 1 接受一个整型值 xff08 无符号 xff09 xff0c 按照顺序打印它的每一位 2 编写函数不允许创建临时变量 xff0c 求字符串的长度 3 求n的阶乘 xff08 不考虑溢出 xff09 4 求第n个斐波那契数 xff08
  • c语言—数组

    目录 1 一维数组的创建和初始化 1 1 数组的创建 1 2 数组的初始化 1 3 一维数组的使用 1 4 一维数组在内存中的存储 2 二维数组的创建和初始化 2 1 二维数组的创建 2 2 二维数组的初始化 2 3 二维数组的使用 2 4
  • 【C语言】三子棋游戏(详解)

    大家好 xff0c 我是小突突 今天我想详细地和你讲解这个三子棋小游戏是怎样实现的 目录 1 基本流程 2 配置运行环境 3 代码过程 3 1菜单界面选择开始或者退出游戏 3 2 创建棋盘并初始化 3 3打印棋盘 4 玩家落子并打印棋盘 5
  • ARM64开发板Ubuntu18.04环境安装docker-compose

    ARM64开发板Ubuntu18 04环境安装docker compose 硬件环境安装docker compose 硬件环境 我使用的是3399开发板 xff0c 开发板安装了ubuntu18 04 xff0c 最近想把程序都倒腾到doc
  • 一篇文章带你搞懂扫雷小游戏(c语言实现)

    目录 前言 1 游戏设计逻辑 2 游戏思考及实现过程 2 1符号与棋盘的建立 2 2棋盘的初始化与打印 2 3布置雷 2 4 排查雷并设置结束标志 3 代码展示 test c game c game h 前言 扫雷是一款经典的小游戏 xff
  • 操作符详解—c语言

    目录 1 操作符分类 xff1a 2 算术操作符 3 移位操作符 3 1 左移操作符 3 2 右移操作符 4 位操作符 5 赋值操作符 6 单目操作符 6 1 单目操作符介绍 7 关系操作符 8 逻辑操作符 9 条件操作符 10 逗号表达式
  • (初阶)指针

    好长时间没有更新博客了 xff0c 博主前段时间考虑了自己的学习路线 xff0c 还是想要去考个研究生 xff0c 以后会一直更新的 本篇文章简单地讲解一下指针的一些基础知识 xff0c 大招还会放在后面 目录 1 指针是什么 xff1f
  • (c语言)初识结构体

    目录 1 结构体的声明 1 1 结构的基础知识 1 2 结构的声明 1 3 结构成员的类型 1 4 结构体变量的定义和初始化 2 结构体成员的访问与传参 1 结构体的声明 1 1 结构的基础知识 结构是一些值的集合 xff0c 这些值称为成
  • (修炼内功)函数栈帧的创建和销毁

    前言 修炼内功才是你和别人拉开差距的地方 越触及底层就会发现计算机竟有如此的有魅力 希望每个看到这篇文章的人都可以好好食用 目录 前言 1 什么是函数栈帧 2 理解函数栈帧能解决什么问题呢 xff1f 3 函数栈帧的创建和销毁解析 3 1
  • 调试技巧总结

    目录 1 调试是什么 xff1f 2 调试的基本步骤 3 Debug和Release的介绍 4 调试实例 4 1 实现代码 xff1a 求 1 xff01 43 2 xff01 43 3 xff01 43 n xff1b 不考虑溢出 4 2