Skip to content

条件语句:让程序学会看情况 ​

额,你写程序,不可能没有if/else,对吧。如果程序永远只按照一条直线走到底,那它就和一个只会复读的机器没什么区别。现实中的程序需要做判断——"用户输入了负数?那就提示错误""传感器读数超过阈值?那就触发报警"。条件语句就是赋予程序这种"做决定"能力的机制。

if 和 if-else——最基本的分支 ​

if 语句的语法非常直白:括号里放一个条件表达式,如果条件为真(也就是能转换成 true),就执行后面的代码块。

C++
#include <iostream>

int main()
{
    int temperature = 38;

    if (temperature > 37) {
        std::cout << "温度偏高,请注意降温" << std::endl;
    }

    return 0;
}

运行结果:

text
温度偏高,请注意降温

条件不满足的时候什么都不做,有时候不够用。我们需要一个"否则"的分支——这就是 else。更进一步,如果还有第三种、第四种情况,就可以用 else if 把多个条件串起来:

C++
int score = 85;

if (score >= 90) {
    std::cout << "等级: A" << std::endl;
} else if (score >= 80) {
    std::cout << "等级: B" << std::endl;
} else if (score >= 70) {
    std::cout << "等级: C" << std::endl;
} else if (score >= 60) {
    std::cout << "等级: D" << std::endl;
} else {
    std::cout << "等级: F" << std::endl;
}

运行结果:

text
等级: B

这里有一个容易被忽略的细节:else if 并不是 C++ 的独立关键字,它实际上是 else 后面跟了一个新的 if 语句。编译器看到的是一棵嵌套的二叉分支树。条件是从上到下依次检查的,一旦某个条件为真,后面的所有分支都会被跳过——如果你把 score >= 60 写在 score >= 90 前面,那 85 分也会被归到 D 级去。

当然,if 括号里的条件必须能转换成 bool:整数非零为 true,指针非空为 true。这个隐式转换后面会引出一个经典坑。

那些年我们踩过的坑——if 的常见陷阱 ​

赋值 vs 比较——编译器不会拦你的笔误 ​

C++
int x = 0;
if (x = 5) {
    std::cout << "x is 5" << std::endl;
}

你可能以为意思是"如果 x 等于 5",但 = 是赋值运算符,== 才是比较运算符。这段代码做的事情是:把 5 赋给 x,然后因为赋值表达式的结果就是被赋的值(5,非零),条件永远为真。更糟糕的是,x 被意外修改成了 5。

if (x = 5) 能通过编译,不会报错,但逻辑几乎一定不是你想要的。务必开启 -Wall -Wextra 编译选项,GCC 和 Clang 遇到这种写法会发出警告。有些程序员习惯把常量写在左边 if (5 == x),这样万一写成 if (5 = x) 编译器会直接报错,因为不能给常量赋值。

悬垂 else 和花括号的习惯 ​

下面这段代码,缩进看起来 else 是和第一个 if 配对的:

C++
if (a > 0)
    if (b > 0)
        result = 1;
else
    result = -1;

但 C++ 的规则是 else 总是和最近的、尚未配对的 if 绑定。所以这段代码实际上等价于:

C++
if (a > 0) {
    if (b > 0) {
        result = 1;
    } else {
        result = -1;
    }
}

如果我们的本意是让 else 和外层 if 配对(a <= 0 时把 result 设为 -1),那这段代码就是完全错误的。所以非常感谢我的同事,他看到我写出来

C++
if(a > 1) return -1;

的时候毫不犹豫的说敢交这个代码,Review别想过。现在我都不太敢写这种不花括号包起来的代码

所以,即使分支体只有一行代码,也要加花括号!也要加花括号!也要加花括号!也要加花括号!也要加花括号!这不是多打几个字符的问题,而是防止歧义和未来维护时引入 bug——你加一行代码的时候忘了补花括号,逻辑就完全变了。

switch 语句——多路分支的利器 ​

当你需要把同一个表达式和多个离散值做比较时,switch 比 if/else if 链更清晰。编译器通常还会把它优化成跳转表(jump table),查表接近 O(1)。

展开代码收起代码共 28 行
C++
// 各位,混个眼熟,enum class您可以先当enum使用,但是他显然比enum更好,可以问问AI为什么
enum class Command {
    kStart,
    kStop,
    kPause,
    kResume
};

void handle_command(Command cmd)
{
    switch (cmd) {
        case Command::kStart:
            std::cout << "启动操作" << std::endl;
            break;
        case Command::kStop:
            std::cout << "停止操作" << std::endl;
            break;
        case Command::kPause:
            std::cout << "暂停操作" << std::endl;
            break;
        case Command::kResume:
            std::cout << "恢复操作" << std::endl;
            break;
        default:
            std::cout << "未知命令" << std::endl;
            break;
    }
}

穿透特性——忘了 break 就会"漏水" ​

每个 case 末尾的 break 用来跳出 switch。如果忘了写,执行完当前 case 后不会停下来,而是"穿透"到下一个 case 继续执行——这就是 fall-through。比如当 cmd 为 Command::kStart 但忘了写 break 时,输出会是:

text
启动
停止

一启动就停了,这就是穿透带来的 bug。

写 switch 必须写 break。养成习惯!

有老手会说:哦哟,这么吓人,很多常见我就是要fall-through(也就是说,不break就是我的意图) 那就记得打一个[[fall-through]]标签,不支持就加个 /* fall through */。这就是我的意思!

case 标签的限制 ​

switch 的 case 标签必须是整数常量表达式——编译时就能确定值的整数,不能用变量、浮点数或字符串。另外,养成写 default 分支的习惯,哪怕它只是打一行日志。特别是当你的枚举后来新增了成员但忘了更新 switch 的时候,default 就是你的安全网。

三元运算符——简洁的条件表达式 ​

三元运算符的语法是 condition ? value_if_true : value_if_false,它是 if/else 的一种表达式形式,适合在两种值之间做选择:

C++
int a = 10;
int b = 20;
int max_val = (a > b) ? a : b;  // max_val = 20

三元运算符能直接嵌入表达式中,在初始化 const 变量时特别有用——const 只能初始化不能赋值,用 if/else 就做不了:

C++
const int kBufferSize = (mode == Mode::kHighSpeed) ? 1024 : 256;

但三元运算符不适合嵌套。像 a ? b ? c : d : e 虽然语法合法,可读性极差。逻辑超过两层选择,老老实实写 if/else。

实战演练——conditional.cpp ​

现在我们把这一章学的东西整合到一个完整程序里:根据输入的成绩分数输出等级,用不同方式实现。

展开代码收起代码共 68 行
C++
#include <iostream>

/// @brief 用 if-else 链判断成绩等级
/// @param score 百分制分数 (0-100)
/// @return 等级字符
char grade_by_if(int score)
{
    if (score >= 90) {
        return 'A';
    } else if (score >= 80) {
        return 'B';
    } else if (score >= 70) {
        return 'C';
    } else if (score >= 60) {
        return 'D';
    } else {
        return 'F';
    }
}

/// @brief 用 switch 判断成绩等级
/// @param score 百分制分数 (0-100)
/// @return 等级字符
char grade_by_switch(int score)
{
    switch (score / 10) {
        case 10:
        case 9:
            return 'A';
        case 8:
            return 'B';
        case 7:
            return 'C';
        case 6:
            return 'D';
        default:
            return 'F';
    }
}

int main()
{
    int score = 0;
    std::cout << "请输入成绩 (0-100): ";
    std::cin >> score;

    if (score < 0 || score > 100) {
        std::cout << "无效的成绩输入" << std::endl;
        return 1;
    }

    char grade = grade_by_if(score);
    std::cout << "if-else 判定结果: " << grade << std::endl;

    grade = grade_by_switch(score);
    std::cout << "switch 判定结果:  " << grade << std::endl;

    std::cout << "是否及格: "
              << (score >= 60 ? "是" : "否") << std::endl;

    if (int diff = score - 60; diff >= 0) {
        std::cout << "超过及格线 " << diff << " 分" << std::endl;
    } else {
        std::cout << "距离及格还差 " << -diff << " 分" << std::endl;
    }

    return 0;
}

编译运行:

bash
g++ -std=c++17 -Wall -Wextra -o conditional conditional.cpp
./conditional

测试输入 85:

text
请输入成绩 (0-100): 85
if-else 判定结果: B
switch 判定结果:  B
是否及格: 是
超过及格线 25 分

测试输入 42:

text
请输入成绩 (0-100): 42
if-else 判定结果: F
switch 判定结果:  F
是否及格: 否
距离及格还差 18 分

很好,三种条件语句都给出了正确一致的结果。注意 grade_by_switch 利用 score / 10 把分数映射到 0-10,再用穿透特性合并 10 和 9。这种技巧在实际项目中偶尔能见到,但如果你觉得不好读,用 if-else 链也没问题,可读性优先。

在线运行 ​

在线运行下面的综合示例,观察 if-else、switch 和三元运算符的判定结果:

Compiler Explorer

条件语句综合演示:if-else / switch / 三元运算符

在线运行并观察成绩等级判定的多种实现方式。试着修改 kScore 的值再看结果。

code/examples/vol1/05_conditionals.cpp

动手试试 ​

光看不练等于没学。以下是三个练习,难度递增,建议每个都动手写一遍。

练习一:正数、负数、零 ​

写一个程序,读取一个整数,判断它是正数、负数还是零。要求分别用 if-else 链和三元运算符两种方式实现。三选一用三元运算符只能嵌套,写完对比一下:哪种你愿意在半年后重读?

预期交互效果:

text
请输入一个整数: -7
-7 是负数
参考答案

main.cpp(if-else 链版本)

C++
#include <iostream>

int main()
{
    int value = 0;
    std::cout << "请输入一个整数: ";
    std::cin >> value;

    if (value > 0) {
        std::cout << value << " 是正数" << std::endl;
    } else if (value < 0) {
        std::cout << value << " 是负数" << std::endl;
    } else {
        std::cout << value << " 是零" << std::endl;
    }

    return 0;
}

main.cpp(三元运算符版本)

C++
#include <iostream>

int main()
{
    int value = 0;
    std::cout << "请输入一个整数: ";
    std::cin >> value;

    std::cout << value
              << (value > 0 ? " 是正数"
                            : value < 0 ? " 是负数"
                                        : " 是零")
              << std::endl;

    return 0;
}

这个版本就是正文说"不适合嵌套"的那种写法——题目点名三元运算符,三选一只好嵌套。对比上面的 if-else 版本,光是数问号和冒号就费眼睛,"可读性极差"说的就是这种手感。体会过就够了:实际代码里超过两层选择,老老实实写 if/else,三元留给两选一。

编译运行:

bash
g++ -std=c++17 -Wall -Wextra main.cpp -o main && ./main

运行结果:

text
请输入一个整数: 10
10 是正数

练习二:简单计算器 ​

用 switch 实现一个简单计算器:从标准输入读取两个整数和一个运算符(+、-、*、/),输出运算结果。除法要处理除数为零的情况。

预期交互效果:

text
请输入表达式(如 3 + 5): 10 / 0
错误:除数不能为零
参考答案

main.cpp

展开代码收起代码共 38 行
C++
#include <iostream>

int main()
{
    int left = 0;
    int right = 0;
    char operation = 0;

    std::cout << "请输入表达式(如 3 + 5): ";
    if (!(std::cin >> left >> operation >> right)) {
        std::cout << "错误:输入格式无效" << std::endl;
        return 1;
    }

    switch (operation) {
    case '+':
        std::cout << left + right << std::endl;
        break;
    case '-':
        std::cout << left - right << std::endl;
        break;
    case '*':
        std::cout << left * right << std::endl;
        break;
    case '/':
        if (right == 0) {
            std::cout << "错误:除数不能为零" << std::endl;
            return 1;
        }
        std::cout << left / right << std::endl;
        break;
    default:
        std::cout << "错误:不支持的运算符" << std::endl;
        return 1;
    }

    return 0;
}

编译运行:

bash
g++ -std=c++17 -Wall -Wextra main.cpp -o main && ./main

运行结果:

text
请输入表达式(如 3 + 5): 10/0
错误:除数不能为零

练习三:日期合法性检查 ​

写一个函数,接收年、月、日三个整数,用条件语句判断这个日期是否合法。需要考虑月份范围是否在 1-12、每月天数上限不同、闰年的二月有 29 天。提示:用 switch 处理不同月份的天数会非常清晰。

参考答案

main.cpp

展开代码收起代码共 40 行
C++
#include <iostream>

int main()
{
    int year, month, day;
    std::cout << "请输入年、月、日(用空格分隔,例如:2024 2 29): ";
    if (!(std::cin >> year >> month >> day)) {
        std::cout << "错误:输入格式无效" << std::endl;
        return 1;
    }

    if (year <= 0 || month < 1 || month > 12 || day < 1) {
        std::cout << "这个日期不合法" << std::endl;
        return 1;
    }

    int maxDay = 31;
    switch (month) {
    case 2:
        maxDay = ((year % 400 == 0) ||
                  (year % 4 == 0 && year % 100 != 0))
                     ? 29
                     : 28;
        break;
    case 4:
    case 6:
    case 9:
    case 11:
        maxDay = 30;
        break;
    }

    if (day > maxDay) {
        std::cout << "这个日期不合法" << std::endl;
        return 1;
    }

    std::cout << "这个日期合法" << std::endl;
    return 0;
}

编译运行:

bash
g++ -std=c++17 -Wall -Wextra main.cpp -o main && ./main

运行结果:

text
请输入年、月、日(用空格分隔,例如:2024 2 29): 2024 2 29
这个日期合法

pdf-latest-4-g85128cc · 85128cc · 2026-10-05