C6 控制

1. if语句:基础的条件分支

if语句被称为分支语句branching statement)或选择语句selection statement),它相当于一个交叉点,程序要在两条分支中选择一条执行。

if语句的通用形式

if (expression)
    statement

如果对expression求值为真,则执行statemetn,否则,跳过statement

if语句和while语句的结构相似,其expression都可以是简单语句或复合语句,不同之处在于,if语句只测试和执行一次,而while语句可以测试和执行多次。

2. if else语句:二选一结构

基础的if语句让程序选择“执行”或“跳过”一条语句,而if else语句则提供了真正的“二选一”能力。

if else语句的通用形式

if (expression)
    statement1
else
    statemetn2

如果expression求值为真,则执行statement1,否则执行statement2

2.1 实现多重选择:else if

当需要从多个条件中选择一个执行时,可以使用else if,它是if else的变式。C99标准要求编译器至少支持127层嵌套。

方式一:标准嵌套

if (expression1)
    statement1
else
    if(expression2)
        statement2
    else
        statement3

方式二:else if写法

if (expression1)
    statement1
else if(expression2)
    statement2
else
    statement3

方式一和方式二在逻辑上完全等价。方式二的写法更清晰,是更常用的风格。

2.2 注意事项:elseif的配对规则

当代码中出现多个ifelse时,编译器遵循 “最近匹配”​ 原则:else总是与它前面最近的、且尚未配对的if​ 进行配对。编写嵌套条件时,使用花括号{}明确代码块范围是避免歧义的最佳实践。

3. 逻辑运算符:构建复杂条件

为了表达“且”、“或”、“非”等复杂的逻辑关系,C语言提供了逻辑运算符。

逻辑运算符意义
&&
||
!

⚠️提示:当程序包含iso646.h头文件时,可以使用and代替&&or代替||not代替!

3.1 逻辑运算符优先级

  • !运算符的优先级高于乘法运算符,与递增运算符的优先级相同,只比圆括号的优先级低。
  • &&运算符的优先级高于||运算符
  • &&||的优先级都比关系运算符低,比赋值运算符高。

示例

a > b && b > c || b > d
// 相当于 ((a > b) && (b > c)) || (b > d)

3.2 求值顺序与短路求值

除两个运算符共享同一个运算对象的情况外,C并不保证先对复杂运算表达式的哪部分先求值:

apples = (5 + 3) + (9 + 6);

C并不保证先求值(5 + 3)还是先求值(9 + 6),C将决定权交给编译器的实现者,以便针对特定系统进行优化。

但是C保证逻辑表达式的求值顺序是从左到右&&||都是序列点,当C发现某个元素让整个表达式无效,便立即停止求值。

短路求值short-circuit evaluation)是一种在逻辑运算中常用的优化技术。它的核心思想是,当逻辑表达式的结果已经可以确定时,立即停止计算剩余的部分,从而提高程序的运行效率。

4. 条件运算符:?::简洁的二选一表达式

C提供条件运算符conditional expression)作为表达if else语句的一种便捷方式,该表达式使用?:条件运算符。该运算符分为两个部分,需要三个对象,属于三元运算符。

条件表达式的通用形式

expression1 ? expression2 : expression3

如果expression1求值为真,则执行expression2,否则执行expression3

5. 循环辅助:continuebreak

在循环结构中,continuebreak语句提供了更精细的流程控制。

5.1 continue语句

当执行到continue语句时,会跳过本次迭代的剩余部分,直接进入循环的条件判断部分,开始下一次循环迭代。

5.2 break语句

当执行到break语句时,会立即终止当前所在的循环,并继续执行该结构之后的代码。

6. 多重选择:switchbreak

当需要基于一个整型表达式的不同值执行不同的代码块时,switch语句比一长串if else if更清晰、高效。

switch语句的通用形式

switch (expression) // expression必须是整型表达式(如int, char)
{
    case value1:
        statement1
        break;
    case value2:
        statement2
        break;
    case value3:
        statement3
        break;
    ……
    default:
        statementN
}

执行流程

  1. 计算switchexpression的值。
  2. 将计算结果与各个case后的常量值value进行精确匹配
  3. 匹配成功后,程序跳转到该case标签处开始执行代码。
  4. 一直执行,直到遇到break;语句,则跳出整个switch块。
  5. 如果所有case都不匹配,则执行default:标签后的代码(如果提供了default部分)。

关键特性case标签只决定入口点,而不是代码块的分隔符。如果不在一个case的代码末尾添加break;,程序会“穿透”到下一个case中继续执行,直到遇见breakswitch结束。这被称为“case穿透”(fallthrough)。

6.1 多重标签:利用“穿透”特性

可以利用“穿透”特性,让多个不同的case值共享同一段执行代码。

switch (ch) {
    case 'a':
    case 'A': 
        printf("你输入了A。\n");
        break;
    case 'b':
    case 'B':
    case 'c':
    case 'C': 
        printf("你输入了B或C。\n");
        break;
    default: 
        printf("其他输入。\n");
}
// 例如,当ch为'a'或'A'时,都会执行第一个printf。

⚠️ 提示:何时用switch?何时用if else

  • switch:当分支条件基于单个整型或枚举表达式与一系列离散常量的精确匹配
  • if else:当条件为范围判断(如score > 60)、浮点数比较、或涉及多个变量的复杂逻辑表达式时。switch无法处理这些情况。

7. goto语句

goto语句允许程序无条件地跳转到同一函数内的某个标签处继续执行。它由关键字goto标签名组成。标签名的命名遵循变量命名规则。

goto语句的通用形式

goto label_name;  // 跳转语句
// ...
label_name: statement // 标签定义

示例:跳出多层嵌套循环

#include <stdio.h>

int main() {
	for (int line = 0; line < 10; line++) {
		for (int col = 0; col < 10; col++) {
			printf(" %d ", col + 1);
			if (line == 3 && col == 4) {
				goto loop_exit; // 满足条件时,直接跳出两层循环
			}
		}
		printf("\n");
	}
	loop_exit: printf("\n已跳出所有循环。\n"); // 跳转目的地
	return 0;
}

⚠️ 建议goto语句会破坏程序的结构化,使代码流程难以跟踪和维护。在现代编程中,应尽量避免使用goto。它的一个可被接受的有限用途是:从深度嵌套的循环(如上面的多层for循环)中一次性跳出,其他情况通常可用breakcontinue、函数返回或状态标志来更清晰地实现。