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

简介: 适合初学者的超详细实用调试技巧

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


大概分为以下几个部分:


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。


为什么呢?


首先推测问题出现的原因。初步确定问题可能的原因最好。

实际上手调试很有必要。

调试的时候我们要做到心里有数

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


c0a57a5b4937183fd0fef506bdb6d827_450e5169783843528983f69feab4de71.png


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


52735e0b5dfbda0c6efbd7dd5d52262e_faa37af4d28542cc8c8f1db79531194c.png


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


c9de939dbf39ae5b43c47692548bcf16_36c5da4ad58b4134b8e8a09daaa6ef19.png


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


d45e67876988cd3dc74c71a978755c21_4995e6e38f0940079242fa90d540e95a.png


在第三次循环,我们发现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;
}


6d85797629532c4d7664da880013cee0_6e8a0b5cbfd54be78670871ebd3c9304.png


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


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


edb3f6d1f249a0cbc2bb3c4c30eedec6_d6096af3cf45471d9ef5c4266bbd6e0e.png


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


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


8b70756ebe6f283549bbcb2d83ed6fea_1de8fce155b74d97b934d1fdf15ad86d.png


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


ba1bf79b5d4a21f0d4c806c09bc82da5_90901f1f9e414a97aed7744df43ce5bb.png


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


相关文章
|
6月前
|
缓存 前端开发 JavaScript
【第22期】 一文读懂前端调试利器whistle
【第22期】 一文读懂前端调试利器whistle
124 0
|
2月前
|
Go iOS开发 MacOS
学习笔记-代码调试工具
dlv(delve的简称)是一个强大的Go源代码级调试工具,通过控制程序执行、计算变量及提供线程/协程状态等信息,为调试Go程序提供了简单且功能丰富的界面。在macOS上,可以通过`brew install dlv`命令安装。下面是一个使用dlv调试Go代码的例子,展示了如何设置断点并逐步执行代码,检查变量值和函数内部细节。常用命令包括:`b`(打断点)、`p`(打印变量)、`n`(下一步)、`c`(继续执行)、`args`(打印参数)、`locals`(打印局部变量)、`l`(列出代码)、`bp`(显示断点)和`q`(退出)。
44 2
|
5月前
|
存储 编译器 C语言
C语言学习记录——调试技巧(VS2019环境下)
C语言学习记录——调试技巧(VS2019环境下)
53 2
|
5月前
|
Shell 数据安全/隐私保护 开发者
详细解读ApolloGPS调试笔记
详细解读ApolloGPS调试笔记
24 0
|
6月前
|
程序员 Windows
程序员必备技能——调试技巧
程序员必备技能——调试技巧
90 0
|
6月前
|
存储 程序员 编译器
程序员必备技能——调试技巧(二)
程序员必备技能——调试技巧(二)
50 0
|
6月前
|
机器学习/深度学习 图计算 异构计算
|
IDE 程序员 编译器
必备技能:程序的调试
本节内容相对来说比较笼统一些,但是也是非常非常非常重要的一节内容,毕竟一个程序猿不能只会写bug,你写一堆bug谁来修呢?所以通过调试去排查错误的能力是一个优秀的程序猿必须要掌握的技能,而且不能仅仅是掌握,更要精通,本节内容总体来说只有两大部分,一是学习调试的方法,会有举例实际去感受一下,二是在写代码就要考虑的一些问题,养成良好的写代码习惯,防患于未然。
必备技能:程序的调试
|
安全 程序员
适合初学者的超详细实用调试技巧(下)二
适合初学者的超详细实用调试技巧
48 0
|
程序员 编译器 C++
适合初学者的超详细实用调试技巧(上)一
适合初学者的超详细实用调试技巧
85 0