适合初学者的超详细实用调试技巧(下)

news2024/12/23 20:35:01

我们日常写代码的时候,常常会遇到bug的情况,这个时候像我这样的初学者就会像无头苍蝇一样这里改改那里删删,调试的重要性也就显现出来,这篇文章接着上文来讲解。

上文地址:(8条消息) 适合初学者的超详细实用调试技巧(上)_陈大大陈的博客-CSDN博客

大概分为以下几个部分:

5. 一些调试的实例。

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

7. 编程常见的错误。

话不多说,现在开始!

5. 一些调试的实例

5.1 实例一 

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

我们失误写出下面的错误代码:

#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++)
 {
 ret *= j;
 }
 sum += ret;
 }
 printf("%d\n", sum);
 return 0;
}

我们输入1和2时,结果并没有错误。 

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

为什么呢?

  1. 首先推测问题出现的原因。初步确定问题可能的原因最好。
  2.  实际上手调试很有必要。
  3.  调试的时候我们要做到心里有数

我们小试牛刀调试一下,首先分析问题所在。 编译器没有报错,说明代码没有语法的问题。

 输入3,按f11逐语句进行调试。

 一次循环下来,sum和ret都变为1,i变为1。

 第二次循环下来,仍然看不到什么问题,阶乘和其和也都正确。

在第三次循环,我们发现ret增长的速度十分的快,这才发现ret的值并没有重置为1 。

我们通过调试发现了错误,写出了正确的代码:


#include<stdio.h>
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;
		ret = 1;//将ret=1置于循环里
		for (j = 1; j <= i; j++)
		{
			ret *= j;
		}
		sum += ret;
	}
	printf("%d\n", sum);
	return 0;
}

5.2.实例2

给出一个数组越界访问的例子,出自《C陷阱与缺陷》 。

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

 如图,程序会死循环打印hehe。

上一个代码可以不用调试看出来错误,但是这个是无法看出来的,只能调试来看。

 可以看到到这一步为止都十分正常,也就是i==12之前是正常的。

然而这一步之后,i和arr[i]就一同变成了0。

知道了问题所在,我们这次通过地址来调试看看。

 可以看到,当i==12时,i的地址和arr[i]的地址是相同的,也就是说,它们在栈区所开辟的空间相同。这样导致的结果就是,  arr[i] = 0的操作将i也一同变成了0,导致死循环


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

6.1 优秀的代码 

优秀的代码应该满足以下条件。 

1. 代码运行正常

2. bug很少

3. 效率高

4. 可读性高

5. 可维护性高

6. 注释清晰

7. 文档齐全 

为了达到这样的条件,我们可以使用以下常见的coding技巧: 

1. 使用assert

2. 尽量使用const

3. 养成良好的编码风格

4. 添加必要的注释

5. 避免编码的陷阱。

6.2 示范 

我们来模拟实现库函数strcpy

函数的参数形式char* strcpy(char*destination,const char*source);

该参数说明了strcpy返回类型是char类型的指针,将源头(不能被改)拷贝到目的地。

strcpy特点和strlen类似,遇到‘\0’就停止。

比较容易想到的写法是:

#include<stdio.h>
#include<string.h>
#include<assert.h>
void my_strcpy(char* a, char* b)
{
	while (*a != '\0')
	{
		*b = *a;
		b++;
		a++;
	}
	*b = *a;
}
int main()
{
	char a[] = "abcdef";
	char b[10];
	my_strcpy(a, b);
	printf("%s", b);
	return 0;
}

 虽然可以实现strcpy函数的内容,但是优化不怎么样,将简单的功能写的非常麻烦,且无法避免空指针的情况,下面为优化后的写法:

#include<stdio.h>
#include<assert.h>
void my_strcpy(const char a[],char b[])
{
    assert(a!=NULL&&b!=NULL);//断言函数来避免空指针的情况
	while (*b++ = *a++)
	{
		;//当*a为\0的时候,while里的值为假,跳出循环
	}
}
int main()
{
	char a[] = "abcdef";
	char b[10]="";//定义第二个数组来拷贝数组
	my_strcpy(a,b);
	printf("%s", b);
	return 0;
}

 值得注意的是,assert断言函数和const的使用,可以大大增加代码的安全性。

6.3 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. const如果放在*的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改 变。但是指针变量本身的内容可变。

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


6.4. const的实例

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int my_strlen(const char a[])//用const来使代码更加安全
{
	int count = 0;
	while (* a++ != '\0')
	{
		count++;
	}
	return count;
}
int main()
{
	char a[]="abcdef";
	int b = my_strlen(a);
	printf("%d", b);
	return 0;
}

7.编程常见的错误 

7.1 编译型错误

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

如:逗号的使用,分号的添加,括号的对应,各类操作符的使用,库函数的使用格式等。

对于这种问题,我们可以直接通过错误列表的提示来定位问题所在,解决问题。 

7.2 链接型错误

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

 如:变量、头文件的包含,文件的引入,常量和宏的定义,库函数名的拼写,自定义函数名的一致等等。 

就例如将main写错为mian这样的错误。 

7.3 运行时错误

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

如:栈溢出,逻辑漏洞,未指针的越界访,未初始化的变量,字符串溢出,数组越界,重复释放内存,使用无效的指针等等。

就像上文里的几个例子。

对于这样的问题,可以通过调试来解决


说了这么多,调试的章节终于结束了!

希望大家都能成为20%的时间在写程序,但是80%的时间在调试的程序员!

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

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

相关文章

Tomcat目录介绍,结构目录有哪些?哪些常用?

bin 启动&#xff0c;关闭和其他脚本。这些 .sh文件&#xff08;对于Unix系统&#xff09;是这些.bat文件的功能副本&#xff08;对于Windows系统&#xff09;。由于Win32命令行缺少某些功能&#xff0c;因此此处包含一些其他文件。 比如说&#xff1a;windows下启动tomcat用的…

3款强大到离谱的电脑软件,个个提效神器,从此远离加班

推荐3款让你偷懒&#xff0c;让你上头的提效电脑软件&#xff0c;个个功能强大&#xff0c;让你远离加班&#xff01; 很多几个小时才能做好的事情&#xff0c;用上它们&#xff0c;只需要5分钟就行&#xff01;&#xff01; 1、JNPF —— 个人最喜欢的低代码软件 它为开发者…

利用OpenCV的函数equalizeHist()对图像作直方图均衡化处理

如果一幅图像的灰度值集中在某个比较窄的区域&#xff0c;则图像的对比度会显得比较小&#xff0c;不便于对图像的分析和处理。 图像的直方图均衡化可以实现将原图像的灰度值范围扩大&#xff0c;这样图像的对比度就得到了提高&#xff0c;从而方便对图像进行后续的分析和处理…

从Docker挂载逃逸原理复现分析到BlueMoon实战

Docker逃逸 什么是Docker Docker 是一个开源的应用容器引擎&#xff0c;让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中&#xff0c;然后发布到任何流行的 Linux或Windows操作系统的机器上&#xff0c;也可以实现虚拟化。容器是完全使用沙箱机制&#xff0c;相互…

Elasticsearch:在满意度调查中实现并使用情绪分析器

如果你通过博客或新闻关注 Elastic&#xff0c;你已经知道在最新版本的 Elasticsearch 中已经提供了用于自然语言处理 (NLP) 的资源。事实上&#xff0c;在我之前的博客文章中&#xff0c;我已经推出了很多关于 NLP 的博文。请详细阅读 “Elastic&#xff1a;开发者上手指南” …

【Linux 多线程同步】使用同步和互斥实现生产消费模型

目录 1.同步的接口 2.多线程但是按顺序来执行 3.生产消费模型 4.使用互斥加同步实现生产消费模型 &#xff08;采用环形队列&#xff09; 同步&#xff1a;在保证数据安全的前提下&#xff0c;让线程能够按照某种特定的顺序访问临界资源&#xff0c;从而有效避免饥饿问题 …

GraphTrip论文笔记【Information Fusion 92 (2023)】

Dual-grained human mobility learning for location-aware trip recommendation with spatial–temporal graph knowledge fusion 期刊&#xff1a;EI检索 Information Fusion 92 (2023) Challenges &#xff08;1&#xff09;异质交互信息的挖掘 POI位置信息、POI类别信息…

重新标记ImageNet:从全局标签到局部标签(附github代码及论文)

欢迎关注“计算机视觉研究院”计算机视觉研究院专栏作者&#xff1a;Edison_GImageNet可以说是最受欢迎的图像分类基准&#xff0c;但它也是一个具有显著噪声的标签。最近的研究表明&#xff0c;许多样本包含多个类&#xff0c;尽管被假定为单个标签基准。因此&#xff0c;他们…

【办公类-18-02】Python VScode 制作“照片整理.py”的exe文件(打包系列)

效果展示&#xff1a;背景需求&#xff1a;最近一年我都是机动班&#xff08;非固定班主任&#xff09;&#xff0c;所以拍的照片不多&#xff0c;只需要每月把手机里的照片用QQ的“我的文件助手”导出来&#xff0c;然后打开VScode&#xff0c;找到“20211020按日期批量整理文…

独居老人一键式报警器

盾王居家养老一键式报警系统&#xff0c;居家养老一键式报警设备 &#xff0c;一键通紧急呼救设备&#xff0c;一键通紧急呼救系统&#xff0c;一键通紧急呼救器 &#xff0c;一键通紧急呼救终端&#xff0c;一键通紧急呼救主机终端产品简介&#xff1a; 老人呼叫系统主要应用于…

java网络编程-nio学习:阻塞和非阻塞

一、阻塞 阻塞模式下&#xff0c;相关方法都会导致线程暂停 ServerSocketChannel.accept 会在没有连接建立时让线程暂停 SocketChannel.read 会在没有数据可读时让线程暂停 阻塞的表现其实就是线程暂停了&#xff0c;暂停期间不会占用 cpu&#xff0c;但线程相当于闲置 单线…

Learning C++ No.11【string类实现】

引言&#xff1a; 北京时间&#xff1a;2023/2/19/8:48&#xff0c;昨天更新了有关进程状态的博客&#xff0c;然后在休息的时候&#xff0c;打开了腾讯视屏&#xff0c;然后看到了了一个电视剧&#xff0c;导致上头&#xff0c;从晚上6点看到了10点&#xff0c;把我宝贵的博客…

vue(5)

文章目录1. 监测数据原理1.1 通过问题引出1.2 开始1.3 Vue.set() 方法1.4 vue 监视 数组1.5 小练习2. 收集表数据3. 过滤器4. 内置指令4.1 v-text4.2 v-html4.3 v-cloak4.4 v-once4.5 v-pre1. 监测数据原理 1.1 通过问题引出 1.2 开始 要想解决上面的这个问题 &#xff0c;需要…

python基于django幼儿园管理系统

目录 1 绪论 1 1.1课题背景 1 1.2课题研究现状 1 1.3初步设计方法与实施方案 2 1.4本文研究内容 2 2 系统开发环境 4 2.1 JAVA简介 4 2.2MyEclipse环境配置 4 2.3 B/S结构简介 4 2.4MySQL数据库 5 2.5 SPRINGBOOT框架 5 3 系统分析 6 3.1系统可行性分析 6 3.1.1经济可行性 6 3…

【2022.12.9】Lammps+Python 在计算g6(r)时遇到的问题

目录写在前面绘制g6( r )执行步骤【updated】如何检查图像的正确性&#xff1a;不是编程问题&#xff0c;而是数学问题的一个小bug废稿2则&#xff1a;写在前面 全部log&#xff1a; 【2022.11.16】LammpsPythonMATLAB在绘制维诺图时遇到的问题 绘制g6( r )执行步骤【updated…

Eureka原理浅析

文章目录1.简介2.组件2.1 Eureka Server2.1.1 主要功能2.1.2 自我保护机制2.1.3 数据同步方式2.1.4 Server的多级缓存和Client实例过期策略2.2 Eureka Client3.补充3.1 CAP偏重点3.2 功能扩展性3.3 工作流程1.简介 Eureka是Netflix开发的服务发现框架&#xff0c;本身是基于RE…

SegNeXt: 重新思考基于卷积注意力的语义分割

论文信息 论文名称&#xff1a;SegNeXt: Rethinking Convolutional Attention Design for Semantic Segmentation 项目GitHub&#xff1a; GitHub - Visual-Attention-Network/SegNeXt: Official Pytorch implementations for "SegNeXt: Rethinking Convolutional Atten…

ESP-C3入门11. 创建最基本的HTTP请求

ESP-C3入门11. 创建最基本的HTTP请求一、menuconfig配置二、配置 CMakeLists1. 设置项目的额外组件目录2. 设置头文件搜索目录三、在 ESP32 上执行 HTTP 请求的基本步骤1. 创建 TCP 连接2. 设置 HTTP 请求3. 发送 HTTP 请求4. 接收 HTTP 响应5. 处理 HTTP 响应6. 关闭 TCP 连接…

35岁以上的大龄测试员们,后来都干什么去了?

为什么软件测试行业看不见白发苍苍的软件测试员?大龄测试员都去哪里了?各个公司会辞退大龄测试员吗? 如果一位 50 多岁的测试员申请 20 多岁或 30 多岁的职位&#xff0c;有多少公司会雇用他们呢?关于这个问题&#xff0c;有很多流言传说&#xff0c;也有一些残酷的现实。…

努力优化和改造不好的环境,去设计新的、积极的、适合自己的环境

你知道环境对你的影响有多大吗&#xff1f;自己的的社交圈也是一个环境如果你待在一个只知道吃喝玩乐&#xff0c;不思进取&#xff0c;天天玩手机、打游戏的圈子里那你很大程度也会被影响&#xff0c;因为你不跟他们一起你就融入不进去&#xff0c;就会被孤立&#xff0c;很多…