语法和语义错误
本节阅读量:
软件错误很普遍。出错很容易,定位错误却很难。本章,主要讲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"; // 这一行永远不会执行到
}
|
现代编译器越来越善于检测某些类型的常见语义错误(例如,使用未初始化的变量)。但大多数情况下,编译器无法捕获这些类型的问题,因为编译器被设计为检查语法,而不是语义。
上述例子中,错误很容易发现。但在大多数有用的程序中,语义错误不易通过目测代码发现。这就需要调试技术。