章节目录

语法和语义错误

本节阅读量:

软件错误很普遍。编写出错误代码很容易,但定位错误却很难。本章,将探索与在C++程序中查找和修复错误相关的主题,包括学习如何使用作为IDE一部分的集成调试器。

尽管调试工具和技术不属于C++标准,但学习查找和修复程序中的错误是成为一名优秀程序员的极其重要部分。因此,会花费一些时间来讨论,以便随着编写的程序变得更加复杂,诊断和修复问题的能力也以类似的速度提高。

如果您有用另一种编程语言调试程序的经验,会熟悉其中的许多内容。


语法和语义错误

编程是具有挑战性的,同时C++是一种有点古怪的语言。把这两者放在一起,会有各种各样犯错误的方法。错误通常分为两类:语法错误和语义错误(逻辑错误)。

当编写出根据C++语言的语法无效的语句时,会发生语法错误。包括丢失分号、使用未声明变量、不匹配的括号或大括号等错误。例如,以下程序有很多语法错误:

1
2
3
4
5
6
7
#include <iostream>

int main()
{
    std::cout < "Hi there"; << x << '\n'; // 有问题的操作符 (<), 多余的分号, 未定义的变量 (x)
    return 0 // 语句结尾无分号
}

幸运的是,编译器通常会捕获语法错误并生成警告或错误,因此可以轻松地识别和修复问题。然后,重新编译,直到消除所有错误。

程序能够正确编译后,让它实际产生所需的结果可能会很棘手。如果语句在语法上有效,但没有做程序员预期的事情时,就会发生语义错误。

有时将导致程序崩溃,例如在数字被零除的情况下:

1
2
3
4
5
6
7
8
9
#include <iostream>

int main()
{
    int a { 10 };
    int b { 0 };
    std::cout << a << " / " << b << " = " << a / b << '\n'; // 数字除以0,在数学上也是无效
    return 0;
}

更常见的是,产生错误的值或行为:

1
2
3
4
5
6
7
8
9
#include <iostream>

int main()
{
    int x; // 未初始化的变量
    std::cout << x << '\n'; // 使用未初始化的变量,会导致未定义的行为

    return 0;
}

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#include <iostream>

int add(int x, int y) // 函数期望是执行加法
{
    return x - y; // 但实际却执行了减法
}

int main()
{
    std::cout << "5 + 3 = " << add(5, 3) << '\n'; // 结果应该是8,但实际却是2

    return 0;
}

1
2
3
4
5
6
7
8
#include <iostream>

int main()
{
    return 0; // 函数在这里返回

    std::cout << "Hello, world!\n"; // 这一行永远不会执行到
}

现代编译器越来越善于检测某些类型的常见语义错误(例如,使用未初始化的变量)。然而,在大多数情况下,编译器将无法捕获这些类型的大多数问题,因为编译器被设计为检查语法,而不是语义。

上述例子中,错误很容易发现。但在大多数有用的程序中,语义错误不容易通过目测代码来发现。这就需要调试技术。


2.13 第2章总结

上一节

3.1 程序调试过程

下一节