C++ 入门(六)— 调试程序(Debugging)

文章目录

  • 语法和语义错误
  • 调试(Debugging)
    • 调试过程
    • 调试策略
    • 常用的调试策略
    • 更多调试策略
  • 使用集成调试器
    • 步进(Stepping)

语法和语义错误

语法错误

编写根据 C++ 语言的语法无效的语句时,会发生语法错误。例如缺少分号、使用未声明的变量、括号或大括号不匹配等。

编译器通常会捕获语法错误并生成警告或错误。
在这里插入图片描述
语义错误

当语句在语法上有效,但没有执行时,就会发生语义错误。

有时这些会导致程序崩溃,例如:

#include <iostream>int main()
{int a { 10 };int b { 0 };//1.在除以零的情况下std::cout << a << " / " << b << " = " << a / b << '\n'; //2.没有提供初始化设定项int x; // no initializer providedstd::cout << x << '\n'; return 0;
}

现代编译器在检测某些类型的常见语义错误(例如,使用未初始化的变量)方面做得越来越好。

但是,在大多数情况下,编译器将无法捕获大多数此类问题,因为编译器旨在强制执行语法,而不是意图。

调试(Debugging)

调试过程

调试的一般方法,确定问题后,调试问题通常包括五个步骤:

  1. 找到根本原因
  2. 了解问题
  3. 确定修复
  4. 修复问题
  5. 复检

示例:

#include <iostream>int add(int x, int y) // 这个函数应该执行加法
{return x - y; // 但使用了错误的运算符
}int main()
{std::cout << "5 + 3 = " << add(5, 3) << '\n'; // 结果应该是8,但是2return 0;
}

按照调试问题通常包括五个步骤进行调试:

  • 找到根本原因:在第 10 行,我们可以看到我们正在传递参数(5 和 3)的文本,因此没有出错的余地。由于函数 add 的输入是正确的,但输出不正确,因此很明显,函数添加一定产生了错误的值。
  • 了解问题:在这种情况下,很明显为什么会生成错误的值 ,因为我们使用了错误的运算符。
  • 确定修复:我们只需将 operator- 更改为 operator+
  • 修复问题 :将 operator- 更改为 operator+ 并确保程序重新编译。
  • 复检:实现更改后,重新运行程序将指示我们的程序现在生成正确的值 8。

此示例微不足道,但说明了诊断任何程序时将经历的基本过程。

调试策略

在调试程序时,在大多数情况下,您的绝大多数时间将花费在试图找到错误的实际位置上。

发现问题后,其余步骤(修复问题并验证问题是否已解决)相比之下通常是微不足道的。

两种找错误实际位置方式:

通过代码检查发现问题

int main()
{getNames(); // 要求用户输入一堆名字sortNames(); // 把它们按字母顺序排序printNames(); // 打印已排序的名称列表return 0;
}

如果希望此程序按字母顺序打印名称,但它以相反的顺序打印它们,则问题可能出在 sortNames 函数中。如果可以将问题范围缩小到特定函数,则只需查看代码即可发现问题。

通过运行程序来查找问题

观察程序运行时的行为,并尝试从中诊断问题。

这种方法可以概括为:
1、弄清楚如何重现问题;
2、运行程序并收集信息以缩小问题所在;
3、重复上述步骤,直到找到问题;

常用的调试策略

调试策略 #1:注释掉代码

如果程序表现出错误行为,减少必须搜索的代码量的一种方法是注释掉一些代码并查看问题是否仍然存在。

int main()
{getNames(); 
//    doMaintenance(); sortNames(); printNames();return 0;
}

调试策略 #2:验证代码流

在更复杂的程序中常见的另一个问题是程序调用函数的次数过多或过少(包括根本不调用)。

在这种情况下,将语句放在函数的顶部以打印函数的名称会很有帮助。这样,当程序运行时,您可以看到正在调用哪些函数。

#include <iostream>int getValue()
{return 4;
}int main()
{std::cout << getValue << '\n';return 0;
}

尽管我们希望此程序打印值 4,但它应该打印值1。
在这里插入图片描述
这时,添加临时调试语句,可以确认 getValue()韩式是否被调用。

#include <iostream>int getValue()
{
std::cerr << "getValue() called\n";return 4;
}int main()
{
std::cerr << "main() called\n";std::cout << getValue << '\n';return 0;
}

在这里插入图片描述
现在我们可以看到函数 getValue 从未被调用过。

修改后:

#include <iostream>int getValue()
{
std::cerr << "getValue() called\n";return 4;
}int main()
{
std::cerr << "main() called\n";std::cout << getValue() << '\n';return 0;
}

现在,这将产生正确的输出:
在这里插入图片描述

调试策略 #3:打印值

对于某些类型的错误,程序可能会计算或传递错误的值。

我们还可以输出变量(包括参数)或表达式的值,以确保它们是正确的。

int add(int x, int y)
{std::cerr << "add() called (x=" << x << ", y=" << y << ")\n";return x + y;
}
int main()
{int z{ add(x, y) };std::cerr << "main::z = " << z << '\n';printResult(z);return 0;
}

打印语句进行调试劣势

  1. 调试语句会使代码混乱。
  2. 调试语句使程序的输出混乱。
  3. 调试语句需要修改代码以添加和删除代码,这可能会引入新的错误。
  4. 调试语句完成后必须将其删除,这使得它们不可重用。

更多调试策略

对调试代码进行条件化

完成调试语句后,需要删除它们,或者注释掉它们。然后,如果您稍后再次想要它们,则必须重新添加它们,或者取消注释它们。

#include <iostream>#define ENABLE_DEBUG // 注释掉以关闭调试int getUserInput()
{
#ifdef ENABLE_DEBUGstd::cerr << "getUserInput() called\n";
#endifstd::cout << "Enter a number: ";int x{};std::cin >> x;return x;
}int main()
{
#ifdef ENABLE_DEBUGstd::cerr << "main() called\n";
#endifint x{ getUserInput() };std::cout << "You entered: " << x << '\n';return 0;
}

使用日志

日志是已发生事件的连续记录,通常带有时间戳。

生成日志的过程称为日志记录。通常,日志会写入磁盘上的文件(称为日志文件),以便以后可以查看。

日志文件的优点

1、由于写入日志文件的信息与程序的输出是分开的,因此可以避免因混合正常输出和调试输出而导致的混乱。
2、日志文件也可以很容易地发送给其他人进行诊断。

使用集成调试器

步进(Stepping)

调试器

调试器是一种计算机程序,它允许程序员控制另一个程序的执行方式,并在该程序运行时检查程序状态。
例如,程序员可以使用调试器逐行执行程序,并在此过程中检查变量的值。通过将变量的实际值与预期值进行比较,或通过代码观察执行路径,调试器可以极大地帮助跟踪语义(逻辑)错误。

调试器背后的功能是双重的:精确控制程序执行的能力,以及查看(并根据需要修改)程序状态的能力。

在VSCode中配置调试器

  • 请按 Ctrl+Shift+P 并选择“C/C++:添加调试配置;
    在这里插入图片描述 - 然后选择“C/C++:g++ 生成和调试活动文件,这应该创建并打开配置文件;
    在这里插入图片描述

  • 将“stopAtEntry”更改为 true:
    在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Docker Nginx 负载均衡搭建(服务宕机-配置高可用) - 附(Python案例,其它语言同理)

目录 一 . 概要 1. 什么是负载均衡 2. 负载均衡有哪些优势&#xff1f; &#xff08;1&#xff09;应用程序可用性 &#xff08;2&#xff09;应用程序可扩展性 &#xff08;3&#xff09;应用程序安全 &#xff08;4&#xff09;应用程序性能 3 . Nginx负载均衡调度策…

【力扣白嫖日记】180.连续出现的数字

前言 练习sql语句&#xff0c;所有题目来自于力扣&#xff08;https://leetcode.cn/problemset/database/&#xff09;的免费数据库练习题。 今日题目&#xff1a; 180.连续出现的数字 表&#xff1a;Logs 列名类型idintnumvarchar 找出所有至少连续出现三次的数字。 返回…

视频会议参会成员入会时无规律掉线解决方案:奇安信防火墙关闭ALG(反向代理)

【问题描述】 单位组织视频会议&#xff0c;出现很严重的问题是参会人员不能全部入会&#xff0c;多家单位依次都出现这种现象&#xff0c;无法入会的单位不固定。 【问题原因】 抓包查询到上级单位的奇安信防火墙出现了问题。 H323协议和SIP协议的视频流数据包经过防火墙以后…

数字热潮:iGaming 能否推动加密货币的普及?

过去十年&#xff0c;iGaming&#xff08;互联网游戏&#xff09;世界有了显著增长&#xff0c;每月有超过一百万的新用户加入。那么&#xff0c;这一主流的秘密是什么&#xff1f;让我们在本文中探讨一下。 领先一步&#xff1a;市场 数字时代正在重新定义娱乐&#xff0c;iG…

Vue3(pinia) 整合 SpringWebsocket链接url动态传参

前言&#xff1a; &#x1f44f;作者简介&#xff1a;我是笑霸final&#xff0c;一名热爱技术的在校学生。 &#x1f4dd;个人主页&#xff1a;个人主页1 || 笑霸final的主页2 &#x1f4d5;系列专栏&#xff1a;java专栏 &#x1f4e7;如果文章知识点有错误的地方&#xff0c;…

挑战杯 基于机器学习与大数据的糖尿病预测

文章目录 1 前言1 课题背景2 数据导入处理3 数据可视化分析4 特征选择4.1 通过相关性进行筛选4.2 多重共线性4.3 RFE&#xff08;递归特征消除法&#xff09;4.4 正则化 5 机器学习模型建立与评价5.1 评价方式的选择5.2 模型的建立与评价5.3 模型参数调优5.4 将调参过后的模型重…

好狄空气能热水器成功上榜2023年消费者心中的十大信赖品牌

随着环保意识的增强和能源消耗的持续关注&#xff0c;空气能热水器以其高效节能、绿色环保的特性赢得了越来越多消费者的青睐。市场上琳琅满目的空气能热水器品牌让消费者在选择时既兴奋又困惑。究竟哪些品牌能在激烈的竞争中脱颖而出&#xff0c;成为消费者心目中的佼佼者呢&a…

GO数组解密:从基础到高阶全解

在本文中&#xff0c;我们深入探讨了Go语言中数组的各个方面。从基础概念、常规操作&#xff0c;到高级技巧和特殊操作&#xff0c;我们通过清晰的解释和具体的Go代码示例为读者提供了全面的指南。无论您是初学者还是经验丰富的开发者&#xff0c;这篇文章都将助您更深入地理解…

YOLO算法改进Backbone系列之:EfficientViT

EfficientViT: Memory Effificient Vision Transformer with Cascaded Group Attention 摘要&#xff1a;视觉transformer由于其高模型能力而取得了巨大的成功。然而&#xff0c;它们卓越的性能伴随着沉重的计算成本&#xff0c;这使得它们不适合实时应用。在这篇论文中&#x…

掌握C语言指针,轻松解锁代码高效性与灵活性

1. 指针与地址 1.1 概念 我们都知道计算机的数据必须存储在内存里&#xff0c;为了正确地访问这些数据&#xff0c;必须为每个数据都编上号码&#xff0c;就像门牌号、身份证号一样&#xff0c;每个编号是唯一的&#xff0c;根据编号可以准确地找到某个数据。而这些编号我们就…

4.测试教程 - 用例篇

文章目录 1.测试用例的基本要素2.测试用例的给我们带来的好处3.测试用例的设计方法3.1基于需求进行测试用例的设计3.1.1功能需求测试分析3.1.2非功能需求测试分析 3.2具体的设计方法3.2.1等价类3.2.2边界值3.2.3错误猜测法3.2.4判定表3.2.5场景设计法3.2.6因果图3.2.7因果图的需…

Bootstrap引入和使用

Bootstrap 基础用法 目录 Bootstrap 基础用法什么是Bootstrap&#xff1f;引入Bootstrap布局容器栅格系统表格样式表单样式text样式按钮样式图标 什么是Bootstrap&#xff1f; Bootstrap是一个开源的前端框架&#xff0c;用于对HTML和CSS代码进行封装&#xff0c;因此可以直接…