软件错误是普遍存在的。制造它们很容易,而发现它们却很难。在本章中,我们将探讨查找和清楚C++程序内的错误有关的话题,包括学习如何使用集成开发环境中的集成调试器。
虽然调试工具和技术并不是C++标准的一部分,但是学会在你编写的程序中发现和消除错误是成为一个成功的程序员的极其重要的一部分。因此,我们会花一点时间来介绍这个主题,这样随着你编写的程序越来越复杂,你诊断和补救问题的能力也会提高。
如果你有使用其他编译语言调试程序的经验,那么其中的大部分内容你都会很熟悉。
编程是很有挑战性的,而且C++是一种有一些怪异的语言。把这两者放在一起,就会有很多方法来作死。错误一般分为两类:语法错误和语义错误(逻辑错误)。
当你写出的语句按照C++语法是无效的时候,就会出现语法错误
,这包括缺少分号、使用未声明的变量、小括号和大括号不匹配等等错误。例如,下面的程序包含了不少语法错误
#include <iostream>
int main()
{
std::cout < "Hi there"; << x; // 无效的操作符(<),多余的分号,未定义的变量
return 0 // 语句结尾缺少分号
}
幸运的是,编译器一般会捕捉到语法错误,并产生警告和错误,所以你很容易发现并修复问题。然后只需要再编译一次,直到把所有的错误都解决掉。
一旦你的程序编译正确,让它实际产出你想要的结果可能有些棘手。当一条语句再语法上是有效的,但是却没有达到程序员的意图的时候,就会发生语义错误
。
有时候,这些会导致你的程序崩溃,比如下面的程序在处以0的情况下
#include <iostream>
int main()
{
int a { 10 };
int b { 0 };
std::cout << a << " / " << b << " = " << a / b; // 除以0没有被定义
return 0;
}
更多的时候,这些只会产生错误的值或者行为:
#include <iostream>
int main()
{
int x;
std::cout << x; // 使用未初始化的变量会导致未定义的结果
return 0;
}
或者
#include <iostream>
int add(int x, int y)
{
return x - y; // 函数要做的是加,写成了减
}
int main()
{
std::cout << add(5, 3); // 预想的是8,结果是2
return 0;
}
或者
#include <iostream>
int main()
{
return 0; // 函数在这个地方返回
std::cout << "Hello,World!"; // 这条语句永远不会被执行
}
现代编译器在检测某些常见语义错误(比如使用未初始化变量)方面已经做的越来越好。然而,在大多数情况下,编译器将无法发现大多数这类情况,因为编译器的设计是为了执行语法,而不是搞明白你的意图。
在上面的例子中,错误是相当容易发现的。但是在大多数程序中,语义错误并不容易通过目测代码来发现。这时候,调试技术就可以派上用场了。