章节目录

语法和语义错误

本节阅读量:

软件错误很普遍。出错很容易,定位错误却很难。本章主要讲解如何在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 程序调试过程

下一节


本节目录