8-2 if 语句与代码块

我们将首先讨论的第一类控制流语句是条件语句conditional statement。条件语句用于指定关联语句是否应被执行。

C++支持两种基本类型的条件语句:if语句(我们在第4.10节——if语句介绍中已介绍过,本节将进一步探讨)和switch语句(将在后续几节中讲解)。


if语句快速回顾

C++中最基础的条件语句是if语句。if语句的结构如下:

if (condition)
    true_statement;

或使用可选的 else 语句:

if (condition)
    true_statement;
else
    false_statement;

如果条件求值为真,则执行 true_statement。如果条件求值为假且存在可选的 else 语句,则执行 false_statement。

以下是一个使用 if 语句和可选 else 语句的简单程序:

#include <iostream>

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

    if (x > 10)
        std::cout << x << " is greater than 10\n";
    else
        std::cout << x << " is not greater than 10\n";

    return 0;
}

这个程序的工作方式完全符合你的预期:

image

image


if or else 的多重条件语句

新手程序员常会尝试如下写法:

#include <iostream>

namespace constants
{
    constexpr int minRideHeightCM { 140 };
}

int main()
{
    std::cout << "Enter your height (in cm): ";
    int x{};
    std::cin >> x;

    if (x >= constants::minRideHeightCM)
        std::cout << "You are tall enough to ride.\n";
    else
        std::cout << "You are not tall enough to ride.\n";
        std::cout << "Too bad!\n"; // focus on this line

    return 0;
}

然而,请考虑程序的以下运行过程:

image

该程序无法按预期运行,因为 true_statement 和 false_statement 只能是单个语句。这里的缩进误导了我们——上述程序的执行效果如同以下写法:

#include <iostream>

namespace constants
{
    constexpr int minRideHeightCM { 140 };
}

int main()
{
    std::cout << "Enter your height (in cm): ";
    int x{};
    std::cin >> x;

    if (x >= constants::minRideHeightCM)
        std::cout << "You are tall enough to ride.\n";
    else
        std::cout << "You are not tall enough to ride.\n";

    std::cout << "Too bad!\n"; // focus on this line

    return 0;
}

这更明确地说明了“太糟糕了!”语句将始终执行。

然而,通常需要根据某些条件执行多个语句。为此,我们可以使用复合语句(代码块):

#include <iostream>

namespace constants
{
    constexpr int minRideHeightCM { 140 };
}

int main()
{
    std::cout << "Enter your height (in cm): ";
    int x{};
    std::cin >> x;

    if (x >= constants::minRideHeightCM)
        std::cout << "You are tall enough to ride.\n";
    else
    { // note addition of block here
        std::cout << "You are not tall enough to ride.\n";
        std::cout << "Too bad!\n";
    }

    return 0;
}

请记住,代码块会被视为单个语句,因此现在它会按预期运行:

image

image


隐式代码块

若程序员未在if语句或else语句的语句部分声明代码块,编译器将隐式声明一个代码块。因此:

if (condition)
    true_statement;
else
    false_statement;

实际上相当于:

if (condition)
{
    true_statement;
}
else
{
    false_statement;
}

大多数时候,这并不重要。然而,新手程序员有时会尝试在隐式代码块中定义变量,例如:

#include <iostream>

int main()
{
    if (true)
        int x{ 5 };
    else
        int x{ 6 };

    std::cout << x << '\n';

    return 0;
}

image

这段代码无法编译,编译器会报错指出标识符 x 未定义。这是因为上述示例等同于:

#include <iostream>

int main()
{
    if (true)
    {
        int x{ 5 };
    } // x destroyed here
    else
    {
        int x{ 6 };
    } // x destroyed here

    std::cout << x << '\n'; // x isn't in scope here

    return 0;
}

在此上下文中,变量 x 的作用域为块作用域,并在块结束时被销毁。当执行到 std::cout 语句时,x 已不复存在。


是否应为单条语句添加代码块

程序员社区中存在争议:if或else语句后的单条语句是否应显式包裹在代码块中。

支持使用代码块的理由众多。

  1. 不使用代码块容易导致无意间添加看似条件语句实则不然的代码。例如:
if (age >= minDrinkingAge)
    purchaseBeer();

现在假设我们赶时间,修改这个程序以添加另一项功能:

if (age >= minDrinkingAge)
    purchaseBeer();
    gamble(); // will always execute

哎呀,我们刚刚允许未成年人赌博了。祝你在监狱里玩得开心!

  1. 不使用代码块会使程序更难调试。假设我们有以下代码片段:
if (age >= minDrinkingAge)
    addBeerToCart(); // conditionally executes

checkout(); // always executes

假设我们怀疑 addBeerToCart() 函数存在问题,于是将其注释掉:

if (age >= minDrinkingAge)
//    addBeerToCart();

checkout(); // conditionally executes now

现在我们让checkout()变成了条件函数,这显然不是我们的本意。

若始终在if或else语句后使用代码块,上述情况均不会发生。

  1. 如果条件语句(C++23新增的if语句变体)要求使用代码块,那么使用代码块能确保if语句与if条件语句的一致性。

反对在单条语句外使用代码块的最佳理由是:添加代码块会通过垂直间隔分散代码,导致单次可见范围缩小,从而降低代码可读性并可能引发其他更严重的错误。

尽管此建议并非绝对普遍,但开发者社群似乎更倾向于始终使用代码块。

最佳实践
建议将与if或else相关的单个语句放在代码块中(尤其在学习阶段)。经验丰富的C++开发者有时会忽略此做法,以换取更紧凑的垂直间距。

一个合理的折中方案是将单行语句与if或else语句置于同一行:

if (age >= minDrinkingAge) purchaseBeer();
else std::cout << "No drinky for you\n".

这种方法在可读性上稍有损失,却能同时规避上述两种弊端。

单行方法的一个合理批评在于其生成的代码更难调试:

  • 由于条件语句和关联语句会在同一执行步骤中运行,难以判断语句究竟是实际执行还是被跳过。
  • 由于条件语句与关联语句位于同一行,无法仅对关联语句设置断点(即仅在语句实际执行时暂停程序)。

但若上述任一问题妨碍调试,可在条件语句与语句间插入换行符(使其分开成行),完成调试后再移除该换行符。


if-else 与 if-if 的区别

新手程序员有时会困惑何时该使用 if-else(if 语句后跟一个或多个 else 语句),何时该使用 if-if(if 语句后跟一个或多个额外的 if 语句)。

  • 当你只想执行第一个满足条件的后续代码时,使用 if-else。
  • 当你希望执行所有满足条件的后续代码时,使用 if-if。

以下程序演示了这种区别:

#include <iostream>

void ifelse(bool a, bool b, bool c)
{
    if (a)      // always evaluates
        std::cout << "a";
    else if (b) // only evaluates when prior if-statement condition is false
        std::cout << "b";
    else if (c) // only evaluates when prior if-statement condition is false
        std::cout << "c";
    std::cout << '\n';
}

void ifif(bool a, bool b, bool c)
{
    if (a) // always evaluates
        std::cout << "a";
    if (b) // always evaluates
        std::cout << "b";
    if (c) // always evaluates
        std::cout << "c";
    std::cout << '\n';
}

int main()
{
    ifelse(false, true, true);
    ifif(false, true, true);

    return 0;
}

image

在调用 ifelse(false, true, true) 时,a 为假,因此不执行关联语句,转而执行关联的 else 语句。b 为真,故输出 b。由于此 if 条件为真,关联的 else 语句不会执行(紧随其后的其他 else 语句同样不会执行)。请注意,我们仅执行了第一个真条件(b)之后的代码。

在调用 ifif(false, true, true) 时,a 为假,因此不执行关联语句,转至下一个 if。b 为真,因此打印 b 并转至下一个 if。c 为真,因此打印 c。注意我们执行了所有真条件(b 和 c)之后的代码。

现在观察这个类似的函数:

char getFirstMatchingChar(bool a, bool b, bool c)
{
    if (a) // always evaluates
        return 'a';
    else if (b) // only evaluates when prior if-statement condition is false
        return 'b';
    else if (c) // only evaluates when prior if-statement condition is false
        return 'c';

    return 0;
}

既然我们使用了if-else结构,显然只希望在第一个条件为真时执行后续代码。但当关联语句均返回值时,我们可以改写为:

char getFirstMatchingChar(bool a, bool b, bool c)
{
    if (a) // always evaluates
        return 'a'; // returns when if-statement is true
    if (b) // only evaluates when prior if-statement condition is false
        return 'b'; // returns when if-statement is true
    if (c) // only evaluates when prior if-statement condition is false
        return 'c'; // returns when if-statement is true

    return 0;
}

虽然表面上看起来像是希望在每个真条件后执行代码,但一旦遇到第一个真条件,其关联语句就会触发函数返回。剩余的if语句将永远不会被评估。因此,这种写法与先前版本的行为完全等效。当每个关联语句都返回值时,许多程序员倾向于省略else关键字,这样既能避免冗余代码,又能使条件语句排列更整齐。

关键要点
当所有关联语句都返回值时,你始终可以仅使用if-if结构,因为else语句本身不提供任何值。

我们将在下一课继续探索if语句的相关内容。

posted @ 2026-02-25 15:17  游翔  阅读(0)  评论(0)    收藏  举报