总结与测验:C++调试技巧回顾

章节回顾

语法错误 是指你编写了一个不符合 C++ 语言语法规则的语句时发生的错误。编译器会捕获这些错误。

语义错误 是指语句在语法上是有效的,但没有实现程序员的意图。

调试 是指从程序中查找并移除错误的过程。

我们可以使用一个五步流程来处理调试:

  1. 找到根本原因。
  2. 理解问题。
  3. 确定修复方法。
  4. 修复问题。
  5. 重新测试。

通常,找到错误是调试中最困难的部分。

静态分析工具 是分析你的代码并寻找可能表明代码存在问题的语义问题的工具。

能够可靠地重现问题,是调试的第一步,也是最重要的一步。

我们可以使用以下策略来帮助查找问题:

  • 注释掉代码。
  • 使用输出语句验证代码流程。
  • 打印值。

当使用打印语句进行调试时,使用std::cerr而不是std::cout。但更好的是,避免通过打印语句进行调试。

日志文件 是记录程序中发生的事件的文件。将信息写入日志文件的过程称为日志记录。

重构 是指在不改变代码行为的情况下重新组织代码的过程。这通常是为了使程序更加有组织、模块化或性能更优。

单元测试 是一种软件测试方法,通过这种方法测试源代码的小单元以确定它们是否正确。

防御性编程 是一种技术,程序员通过这种方式试图预见软件可能被滥用的所有方式。这些滥用通常可以被检测出来并加以缓解。

程序中跟踪的所有信息(变量值、哪些函数已被调用、当前的执行点)都是程序状态的一部分。

调试器 是一种工具,它允许程序员控制程序的执行,并在程序运行时检查程序状态。集成调试器 是集成到代码编辑器中的调试器。

单步执行 是一组相关调试功能的名称,它允许你逐条语句地执行代码。

  • 进入函数:按程序的正常执行路径执行下一条语句,然后暂停执行。如果语句包含函数调用,进入函数会使程序跳到被调用函数的顶部。
  • 跳过函数:按程序的正常执行路径执行下一条语句,然后暂停执行。如果语句包含函数调用,跳过函数会执行该函数并在函数执行完毕后将控制权交还给你。
  • 退出函数:执行当前正在执行的函数中剩余的所有代码,然后在函数返回时将控制权交还给你。
  • 运行到光标位置:执行程序,直到执行到达光标所选的语句。
  • 继续:运行程序,直到程序终止或遇到断点。开始 与继续相同,只是从程序的开头开始。
  • 断点:是一个特殊的标记,它告诉调试器在达到断点时停止程序的执行。
  • 设置下一条语句:允许我们将执行点更改为其他语句(有时非正式地称为跳转)。这可以用来向前跳转执行点并跳过一些原本会执行的代码,或者向后跳转并重新执行已经执行过的内容。
  • 监视变量:允许你在调试模式下程序执行时检查变量的值。监视窗口允许你检查变量或表达式的值。
  • 调用堆栈:是一个列表,列出了所有被调用以到达当前执行点的活动函数。调用堆栈窗口是一个调试器窗口,它显示调用堆栈。

测验时间

问题 #1

以下程序本应添加两个数字,但无法正常工作。

使用集成调试器逐步执行此程序并监视x的值。根据你学到的信息,修复以下程序:

#include <iostream>

int readNumber(int x)
{
	std::cout << "Please enter a number: ";
	std::cin >> x;
	return x;
}

void writeAnswer(int x)
{
	std::cout << "The sum is: " << x << '\n';
}

int main()
{
	int x {};
	readNumber(x);
	x = x + readNumber(x);
	writeAnswer(x);

	return 0;
}

显示答案

问题 #2

以下程序本应除以两个数字,但无法正常工作。

使用集成调试器逐步执行此程序。对于输入,输入 8 和 4。根据你学到的信息,修复以下程序:

#include <iostream>

int readNumber()
{
	std::cout << "Please enter a number: ";
	int x {};
	std::cin >> x;
	return x;
}

void writeAnswer(int x)
{
	std::cout << "The quotient is: " << x << '\n';
}

int main()
{
	int x{ };
	int y{ };
	x = readNumber();
	x = readNumber();
	writeAnswer(x/y);

	return 0;
}

显示答案

问题 #3

当执行点在第 4 行时,以下程序的调用堆栈是什么样的?对于这个练习,只需要函数名称,不需要表示返回点的行号。

我们在第 3.9 节 — 使用集成调试器:调用堆栈中讨论了调用堆栈。

#include <iostream>

void d()
{ // here
}

void c()
{
}

void b()
{
	c();
	d();
}

void a()
{
	b();
}

int main()
{
	a();

	return 0;
}

显示答案

问题 #4

额外加分:以下程序本应添加两个数字,但无法正常工作。

使用集成调试器逐步执行此程序。对于输入,输入 8 和 4。根据你学到的信息,修复以下程序:

#include <iostream>

int readNumber()
{
    std::cout << "Please enter a number: ";
    char x{};
    std::cin >> x;

    return x;
}

void writeAnswer(int x)
{
    std::cout << "The sum is: " << x << '\n';
}

int main()
{
    int x { readNumber() };
    int y { readNumber() };
    writeAnswer(x + y);

    return 0;
}

显示答案

作者注

鉴于到目前为止所涵盖的有限内容,很难找到具有非显而易见问题的简单程序的例子。读者有任何建议吗?

关注公众号,回复"cpp-tutorial"

可领取价值199元的C++学习资料

公众号二维码

扫描上方二维码或搜索"cpp-tutorial"