Skip to content

7.3 - 常见的 if 语句错误

Key Takeaway

继续 7.2 - if 语句和语句块 的内容。本节课我们会介绍一些和if语句相关的常见错误。

嵌套if语句和悬垂else问题

if 语句可以嵌套在其他 if 语句中:

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

int main()
{
    std::cout << "Enter a number: ";
    int x{};
    std::cin >> x;

    if (x >= 0) // 外层if语句
        // 这种嵌套方式是不好的编程风格
        if (x <= 20) // 内层if语句
            std::cout << x << " is between 0 and 20\n";

    return 0;
}

考虑下面的程序:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#include <iostream>

int main()
{
    std::cout << "Enter a number: ";
    int x{};
    std::cin >> x;

    if (x >= 0) // 外层if语句
        // 这种嵌套方式是不好的编程风格
        if (x <= 20) // 内层if语句
            std::cout << x << " is between 0 and 20\n";

    // 这个 else 应该和哪个 if 语句匹配?
    else
        std::cout << x << " is negative\n";

    return 0;
}

上面的程序引入了一个潜在的歧义来源,称为悬垂else问题。上面程序中的else 语句应该与外部if匹配还是和内部if匹配?

答案是 else 语句与同一语句块中最后一个未匹配的if语句配对。因此,在上面的程序中,else与内部的if语句匹配,等价于下面形式:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#include <iostream>

int main()
{
    std::cout << "Enter a number: ";
    int x{};
    std::cin >> x;

    if (x >= 0) // 外层 if 语句
    {
        if (x <= 20) // 内层 if 语句
            std::cout << x << " is between 0 and 20\n";
        else // 和内层 if 语句匹配
            std::cout << x << " is negative\n";
    }

    return 0;
}

这使得程序产生错误的结果:

1
2
Enter a number: 21
21 is negative

为了避免因嵌套 if 语句产生的歧义,最好的办法是将内部的if语句显式地定义在语句块中:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>

int main()
{
    std::cout << "Enter a number: ";
    int x{};
    std::cin >> x;

    if (x >= 0)
    {
        if (x <= 20)
            std::cout << x << " is between 0 and 20\n";
        else // attached to inner if statement
            std::cout << x << " is greater than 20\n";
    }
    else // attached to outer if statement
        std::cout << x << " is negative\n";

    return 0;
}

此时,语句块中的 else 语句会和内层的if 语句匹配,而语句块外部的 else 则和外层的 if 语句匹配。

嵌套语句展开

嵌套的多层 if 语句可以通过重新组织逻辑或使用逻辑运算符展开成一层逻辑。嵌套越少的代码越不容易出错。

例如,上面的例子中可以展开成如下形式:

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

int main()
{
    std::cout << "Enter a number: ";
    int x{};
    std::cin >> x;

    if (x < 0)
        std::cout << x << " is negative\n";
    else if (x <= 20) // only executes if x >= 0
        std::cout << x << " is between 0 and 20\n";
    else // only executes if x > 20
        std::cout << x << " is greater than 20\n";

    return 0;
}

下面是另一个使用逻辑运算符在一个if语句中检查多个条件的例子:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>

int main()
{
    std::cout << "Enter an integer: ";
    int x{};
    std::cin >> x;

    std::cout << "Enter another integer: ";
    int y{};
    std::cin >> y;

    if (x > 0 && y > 0) // && 是逻辑与——检查是否两个条件均为真
        std::cout << "Both numbers are positive\n";
    else if (x > 0 || y > 0) // || 是逻辑或——检查是否有为真的条件
        std::cout << "One of the numbers is positive\n";
    else
        std::cout << "Neither number is positive\n";

    return 0;
}

空语句

空语句是一个只包含分号的语句:

1
2
if (x > 10)
    ; // this is a null statement

空语句什么都不做。一般来说,只有当语言要求存在一条语句但程序员不需要的情况下才这么做。为了可读性,“空语句”通常被放在单独的行中。

我们将在本章后面讲到循环的时候看到有意的空语句的例子。“空语句”很少与“if语句”一起使用。然而,它们可能会无意中给新手(或粗心的)程序员带来问题。考虑下面的代码:

1
2
if (nuclearCodesActivated());
    blowUpTheWorld();

在上面的例子中,程序员不经意间在if语句的末尾添加了一个分号。这个不起眼的错误可以很好地编译,并使代码段执行起来就像它是这样编写的一样:

1
2
3
if (nuclearCodesActivated())
    ; // 分号实际上制造了一个空语句
blowUpTheWorld(); // 这个函数总是会执行

注意

注意不要在if语句后面添加分号,否则你的“条件语句”总是会无条件执行(即使你把它写在语句块中)。

条件语句中的等号和赋值号

在 if 的条件中,你应该使用等号==来判断是否相等,而不是赋值号=,考虑下面程序:

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

int main()
{
    std::cout << "Enter 0 or 1: ";
    int x{};
    std::cin >> x;
    if (x = 0) // 糟糕, 我们使用赋值号进行了赋值而不是判断相等
        std::cout << "You entered 0";
    else
        std::cout << "You entered 1";

    return 0;
}

程序可以编译运行,但是某些情况下结果是错误的:

1
2
Enter 0 or 1: 0
You entered 1

事实上,这个程序总是会产生“你输入了1”的结果。这是因为x = 0 首先将值 0 赋值给x ,然后计算 x的值,现在是0 ,即布尔值false 。由于条件总是false 所以else语句总是执行。