第4章 控制语句

条件语句

条件语句用于根据不同的条件执行不同的代码块,是实现程序逻辑分支的基础。

if 语句

if 语句是最基本的条件语句,用于根据条件执行不同的代码块。

基本形式

1
2
3
4
if (condition)
{
// 条件为真时执行的代码
}

其中 condition 是一个表达式,其结果为布尔值(0 表示假,非 0 表示真)。

if-else 语句

当需要在条件为假时执行另一段代码时,可以使用 if-else 语句:

1
2
3
4
5
6
7
8
if (condition)
{
// 条件为真时执行的代码
}
else
{
// 条件为假时执行的代码
}

if-else if-else 语句

当需要处理多个条件时,可以使用 if-else if-else 语句:

1
2
3
4
5
6
7
8
9
10
11
12
13
if (condition1)
{
// 条件1为真时执行的代码
}
else if (condition2)
{
// 条件2为真时执行的代码
}
// 可以有多个 else if 分支
else
{
// 所有条件都为假时执行的代码
}

嵌套 if 语句

if 语句可以嵌套使用,即在一个 if 语句内部包含另一个 if 语句:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if (condition1)
{
if (condition2)
{
// condition1 和 condition2 都为真时执行的代码
}
else
{
// condition1 为真但 condition2 为假时执行的代码
}
}
else
{
// condition1 为假时执行的代码
}

示例:成绩等级判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int score = 85;

if (score >= 90)
{
printf("优秀\n");
}
else if (score >= 80)
{
printf("良好\n");
}
else if (score >= 60)
{
printf("及格\n");
}
else
{
printf("不及格\n");
}

示例:嵌套 if 语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int age = 25;
bool has_license = true;

if (age >= 18)
{
if (has_license)
{
printf("可以开车\n");
}
else
{
printf("成年但没有驾照,不能开车\n");
}
}
else
{
printf("未成年,不能开车\n");
}

条件运算符与 if 语句的比较

对于简单的条件判断,可以使用条件运算符(三元运算符)代替 if-else 语句:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 使用 if-else 语句
int max;
if (a > b)
{
max = a;
}
else
{
max = b;
}

// 使用条件运算符
int max = (a > b) ? a : b;

最佳实践

  1. 条件表达式的可读性:复杂的条件表达式应该拆分为多个简单的表达式,或使用辅助变量提高可读性
  2. 代码缩进:使用一致的缩进风格,使代码结构清晰
  3. 大括号的使用:即使只有一条语句,也建议使用大括号包围,避免后续修改时引入错误
  4. 条件顺序:将最可能为真的条件放在前面,减少条件判断的次数
  5. 避免深度嵌套:深度嵌套的 if 语句会降低代码可读性,考虑使用早期返回或重构为函数

switch 语句

switch 语句用于根据表达式的值选择执行不同的代码块,适合处理多个固定值的情况。

基本形式

1
2
3
4
5
6
7
8
9
10
11
12
13
switch (expression)
{
case value1:
// 表达式等于 value1 时执行的代码
break;
case value2:
// 表达式等于 value2 时执行的代码
break;
// 更多 case 语句
default:
// 表达式不等于任何 case 值时执行的代码
break;
}

示例:成绩等级判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
char grade = 'B';

switch (grade)
{
case 'A':
printf("优秀\n");
break;
case 'B':
printf("良好\n");
break;
case 'C':
printf("及格\n");
break;
case 'D':
case 'F':
printf("不及格\n");
break;
default:
printf("无效成绩\n");
break;
}

示例:月份天数判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
int month = 2;
int days;

switch (month)
{
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
days = 31;
break;
case 4:
case 6:
case 9:
case 11:
days = 30;
break;
case 2:
days = 28; // 简化处理,未考虑闰年
break;
default:
days = 0;
printf("无效月份\n");
break;
}

printf("%d月有%d天\n", month, days);

注意事项

  1. 表达式类型expression 必须是整型、字符型或枚举类型,不能是浮点型或字符串
  2. case 标签case 标签必须是常量表达式,不能是变量
  3. break 语句:每个 case 分支通常需要使用 break 语句结束,否则会继续执行下一个 case 分支(称为”穿透”)
  4. default 分支default 分支是可选的,用于处理所有未匹配的情况
  5. case 标签的顺序case 标签的顺序不影响执行结果,但通常将最常见的情况放在前面
  6. 空 case 分支:可以有多个 case 标签对应同一个代码块,如示例中的 ‘D’ 和 ‘F’ 都对应”不及格”的处理

switch 语句与 if-else 语句的比较

特性switch 语句if-else 语句
适用场景多个固定值的判断任意条件的判断
执行速度通常更快(使用跳转表)可能较慢(顺序判断)
代码可读性多个固定值时更清晰复杂条件时更灵活
表达能力仅限于等值比较支持任意逻辑表达式

高级用法:使用枚举类型

switch 语句与枚举类型配合使用可以提高代码的可读性和安全性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
enum Weekday {
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY
};

void print_weekday(enum Weekday day)
{
switch (day)
{
case MONDAY:
printf("星期一\n");
break;
case TUESDAY:
printf("星期二\n");
break;
case WEDNESDAY:
printf("星期三\n");
break;
case THURSDAY:
printf("星期四\n");
break;
case FRIDAY:
printf("星期五\n");
break;
case SATURDAY:
printf("星期六\n");
break;
case SUNDAY:
printf("星期日\n");
break;
default:
printf("无效的星期\n");
break;
}
}

循环语句

循环语句用于重复执行一段代码,是实现重复操作的基础。

while 循环

while 循环在条件为真时重复执行代码块,适合不确定循环次数的情况。

基本形式

1
2
3
4
while (condition)
{
// 条件为真时重复执行的代码
}

示例:打印数字

1
2
3
4
5
6
7
int i = 1;

while (i <= 5)
{
printf("%d\n", i);
i++;
}

示例:计算阶乘

1
2
3
4
5
6
7
8
9
10
11
int n = 5;
int factorial = 1;
int i = 1;

while (i <= n)
{
factorial *= i;
i++;
}

printf("%d的阶乘是%d\n", n, factorial);

示例:读取输入直到遇到特定值

1
2
3
4
5
6
7
8
9
10
11
12
int number;
printf("请输入数字(输入0结束):\n");

scanf("%d", &number);
while (number != 0)
{
printf("你输入的是:%d\n", number);
printf("请输入下一个数字(输入0结束):\n");
scanf("%d", &number);
}

printf("程序结束\n");

注意事项

  1. 循环条件:循环条件必须能够在某个时刻变为假,否则会导致无限循环
  2. 循环变量的更新:确保在循环体内部更新循环变量,否则可能导致无限循环
  3. 空循环体:如果循环体为空,需要使用分号表示,如 while (condition);
  4. 条件的计算:循环条件在每次循环开始时都会重新计算

do-while 循环

do-while 循环先执行一次代码块,然后在条件为真时重复执行,适合至少需要执行一次的情况。

基本形式

1
2
3
4
do
{
// 至少执行一次的代码
} while (condition);

示例:打印数字

1
2
3
4
5
6
7
int i = 1;

do
{
printf("%d\n", i);
i++;
} while (i <= 5);

示例:菜单选择

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
int choice;

do
{
printf("===== 菜单 =====\n");
printf("1. 选项1\n");
printf("2. 选项2\n");
printf("3. 退出\n");
printf("请选择:");
scanf("%d", &choice);

switch (choice)
{
case 1:
printf("你选择了选项1\n");
break;
case 2:
printf("你选择了选项2\n");
break;
case 3:
printf("退出程序\n");
break;
default:
printf("无效选择\n");
break;
}

printf("\n");
} while (choice != 3);

do-while 循环与 while 循环的比较

特性do-while 循环while 循环
执行次数至少执行一次可能一次也不执行
条件检查时机循环体执行后循环体执行前
适用场景至少需要执行一次的操作,如菜单选择不确定是否需要执行的操作
语法结构以 do 开始,while 结束,分号结尾以 while 开始,条件后直接跟随循环体

for 循环

for 循环是一种更结构化的循环形式,适合已知循环次数的情况。

基本形式

1
2
3
4
for (initialization; condition; update)
{
// 条件为真时执行的代码
}

其中:

  • initialization - 循环开始前执行的初始化语句,通常用于初始化循环变量
  • condition - 每次循环前检查的条件,为真时执行循环体
  • update - 每次循环后执行的更新语句,通常用于更新循环变量

示例:打印数字

1
2
3
4
for (int i = 1; i <= 5; i++)
{
printf("%d\n", i);
}

示例:计算累加和

1
2
3
4
5
6
int sum = 0;
for (int i = 1; i <= 100; i++)
{
sum += i;
}
printf("1到100的和是:%d\n", sum);

示例:遍历数组

1
2
3
4
5
6
7
int numbers[] = {1, 2, 3, 4, 5};
int length = sizeof(numbers) / sizeof(numbers[0]);

for (int i = 0; i < length; i++)
{
printf("numbers[%d] = %d\n", i, numbers[i]);
}

for 循环的变体

省略初始化语句

如果循环变量已经在外部初始化,可以省略初始化语句:

1
2
3
4
5
int i = 1;
for (; i <= 5; i++)
{
printf("%d\n", i);
}
省略条件语句

省略条件语句会导致无限循环,需要在循环体内部使用 break 语句退出:

1
2
3
4
5
6
for (int i = 1;; i++)
{
printf("%d\n", i);
if (i >= 5)
break;
}
省略更新语句

如果在循环体内部更新循环变量,可以省略更新语句:

1
2
3
4
5
for (int i = 1; i <= 5;)
{
printf("%d\n", i);
i++;
}
省略所有部分

省略所有三个部分会导致无限循环:

1
2
3
4
5
for (;;)
{
printf("无限循环\n");
// 需要使用 break 语句退出循环
}
多个循环变量

可以在初始化语句中声明多个循环变量,在更新语句中更新多个循环变量:

1
2
3
4
for (int i = 1, j = 10; i <= j; i++, j--)
{
printf("i = %d, j = %d\n", i, j);
}
使用逗号表达式

可以在初始化和更新语句中使用逗号表达式执行多个操作:

1
2
3
4
5
6
int sum = 0;
for (int i = 1, j = 1; i <= 10; sum += i, i++, j++)
{
printf("i = %d, j = %d, sum = %d\n", i, j, sum);
}
printf("最终 sum = %d\n", sum);

注意事项

  1. 循环变量的作用域:在 for 循环的初始化语句中声明的变量,其作用域仅限于循环体内部
  2. 循环条件的计算:循环条件在每次循环开始时都会重新计算
  3. 无限循环:如果省略条件语句或条件永远为真,会导致无限循环
  4. 嵌套 for 循环:for 循环可以嵌套使用,实现更复杂的逻辑

嵌套循环

循环可以嵌套使用,即在一个循环内部包含另一个循环,用于处理多维数据或实现复杂的算法。

示例:打印乘法表

1
2
3
4
5
6
7
8
9
// 打印 9x9 乘法表
for (int i = 1; i <= 9; i++)
{
for (int j = 1; j <= i; j++)
{
printf("%d × %d = %d\t", j, i, i * j);
}
printf("\n");
}

示例:打印二维数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int matrix[3][3] = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};

for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
printf("%d ", matrix[i][j]);
}
printf("\n");
}

示例:打印菱形

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
int n = 5;  // 菱形的半高

// 上半部分
for (int i = 1; i <= n; i++)
{
// 打印空格
for (int j = 1; j <= n - i; j++)
{
printf(" ");
}
// 打印星号
for (int j = 1; j <= 2 * i - 1; j++)
{
printf("*");
}
printf("\n");
}

// 下半部分
for (int i = n - 1; i >= 1; i--)
{
// 打印空格
for (int j = 1; j <= n - i; j++)
{
printf(" ");
}
// 打印星号
for (int j = 1; j <= 2 * i - 1; j++)
{
printf("*");
}
printf("\n");
}

嵌套循环的性能考虑

  1. 循环顺序:对于多维数组的遍历,应按照内存布局的顺序访问,以提高缓存命中率
  2. 循环深度:尽量减少循环的嵌套深度,深度过大会降低代码可读性和性能
  3. 循环展开:对于内层循环,可以考虑循环展开以减少循环开销
  4. 并行处理:对于独立的循环迭代,可以考虑使用并行处理技术

循环控制

循环的选择

循环类型适用场景优点缺点
while不确定循环次数,条件可能在循环体外部变化结构简单,条件检查灵活循环变量的初始化和更新分散在循环外部
do-while至少需要执行一次的操作确保循环体至少执行一次语法相对复杂
for已知循环次数,或循环变量有明确的范围结构紧凑,初始化、条件检查和更新集中在一起对于复杂的循环条件可能不够灵活

循环的优化

  1. 减少循环内的计算:将循环不变的计算移到循环外部
  2. 使用合适的数据结构:选择适合操作的数据结构,如使用数组代替链表进行随机访问
  3. 避免不必要的函数调用:尽量减少循环内的函数调用,或使用内联函数
  4. 循环展开:对于小循环,可以手动展开以减少循环开销
  5. 使用编译器优化:开启编译器的优化选项,如 -O2-O3

跳转语句

跳转语句用于改变程序的执行流程,包括 breakcontinuegoto 语句。

break 语句

break 语句用于跳出当前循环或 switch 语句,执行循环或 switch 语句之后的代码。

基本用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 跳出循环
while (condition)
{
if (some_condition)
break;
// 其他代码
}

// 跳出 switch 语句
switch (expression)
{
case value:
// 代码
break;
// 其他 case
}

示例:跳出 for 循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 找到第一个大于 10 的元素
int numbers[] = {5, 8, 12, 3, 7};
int length = sizeof(numbers) / sizeof(numbers[0]);
int found = -1;

for (int i = 0; i < length; i++)
{
if (numbers[i] > 10)
{
found = i;
break; // 找到后跳出循环
}
}

if (found != -1)
{
printf("第一个大于 10 的元素是 numbers[%d] = %d\n", found, numbers[found]);
}
else
{
printf("没有找到大于 10 的元素\n");
}

示例:跳出嵌套循环

break 语句只能跳出当前层的循环,要跳出多层循环需要使用标签或其他方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 使用标签跳出多层循环
int i, j;
for (i = 0; i < 10; i++)
{
for (j = 0; j < 10; j++)
{
if (i * j > 20)
{
goto end_loop; // 跳转到标签处
}
printf("i = %d, j = %d\n", i, j);
}
}
end_loop:
printf("跳出循环,i = %d, j = %d\n", i, j);

continue 语句

continue 语句用于跳过当前循环的剩余部分,直接进入下一次循环的条件检查。

基本用法

1
2
3
4
5
6
while (condition)
{
if (some_condition)
continue;
// 其他代码
}

示例:跳过偶数

1
2
3
4
5
6
7
// 打印 1 到 10 之间的奇数
for (int i = 1; i <= 10; i++)
{
if (i % 2 == 0)
continue; // 跳过偶数
printf("%d\n", i);
}

示例:处理有效输入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 读取 5 个正整数
int count = 0;
int number;

while (count < 5)
{
printf("请输入一个正整数:");
scanf("%d", &number);

if (number <= 0)
{
printf("无效输入,请重新输入\n");
continue; // 跳过无效输入
}

printf("你输入的是:%d\n", number);
count++;
}

printf("已输入 5 个正整数\n");

goto 语句

goto 语句用于跳转到函数内的指定标签位置,改变程序的执行流程。

基本形式

1
2
3
4
goto label;
// 代码
label:
// 跳转到这里

其中 label 是一个标识符,后面跟着冒号。

示例:使用 goto 实现循环

1
2
3
4
5
6
int i = 1;
loop:
printf("%d\n", i);
i++;
if (i <= 5)
goto loop;

示例:错误处理

goto 语句在错误处理中非常有用,可以集中处理资源释放:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
void process_file(const char* filename)
{
FILE* file = NULL;
char* buffer = NULL;

// 打开文件
file = fopen(filename, "r");
if (file == NULL)
{
perror("无法打开文件");
goto cleanup;
}

// 分配内存
buffer = malloc(1024);
if (buffer == NULL)
{
perror("无法分配内存");
goto cleanup;
}

// 处理文件
// ...

cleanup:
// 释放资源
if (buffer != NULL)
free(buffer);
if (file != NULL)
fclose(file);
}

注意事项

  1. 代码可读性:过度使用 goto 语句会使代码结构变得混乱,降低可读性
  2. 作用域goto 语句只能在同一个函数内跳转,不能跨函数跳转
  3. 跳转方向:虽然 goto 语句可以向前或向后跳转,但向后跳转可能导致无限循环
  4. 变量初始化:跳转到标签处时,需要确保所有使用的变量都已正确初始化
  5. 合理使用goto 语句在以下场景中是合理的:
    • 错误处理和资源释放
    • 跳出多层循环
    • 实现状态机

goto 语句的替代方案

在许多情况下,可以使用其他控制语句替代 goto 语句:

  1. 使用函数:将代码块提取为函数,使用 return 语句退出
  2. 使用循环:使用循环替代向后跳转
  3. 使用条件语句:使用条件语句替代简单的跳转
  4. 使用状态变量:使用状态变量控制程序的执行流程

控制语句的应用

示例 1:猜数字游戏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void)
{
int number, guess, attempts = 0;

// 生成 1-100 之间的随机数
srand(time(NULL));
number = rand() % 100 + 1;

printf("猜数字游戏!\n");
printf("我想了一个 1-100 之间的数字,你能猜对吗?\n");

do
{
printf("请输入你的猜测:");
scanf("%d", &guess);
attempts++;

if (guess > number)
{
printf("太大了!\n");
}
else if (guess < number)
{
printf("太小了!\n");
}
else
{
printf("恭喜你猜对了!\n");
printf("你用了 %d 次尝试。\n", attempts);
}
} while (guess != number);

return 0;
}

示例 2:打印金字塔

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdio.h>

int main(void)
{
int rows, i, j, spaces;

printf("请输入金字塔的行数:");
scanf("%d", &rows);

for (i = 1; i <= rows; i++)
{
// 打印空格
for (spaces = 1; spaces <= rows - i; spaces++)
{
printf(" ");
}

// 打印星号
for (j = 1; j <= 2 * i - 1; j++)
{
printf("*");
}

printf("\n");
}

return 0;
}

示例 3:判断素数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <stdio.h>

int main(void)
{
int number, i, is_prime = 1;

printf("请输入一个正整数:");
scanf("%d", &number);

if (number <= 1)
{
is_prime = 0;
}
else
{
for (i = 2; i <= number / 2; i++)
{
if (number % i == 0)
{
is_prime = 0;
break;
}
}
}

if (is_prime)
{
printf("%d 是素数。\n", number);
}
else
{
printf("%d 不是素数。\n", number);
}

return 0;
}

示例 4:冒泡排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <stdio.h>

void bubble_sort(int arr[], int n)
{
int i, j, temp;
int swapped;

for (i = 0; i < n - 1; i++)
{
swapped = 0;

for (j = 0; j < n - i - 1; j++)
{
if (arr[j] > arr[j + 1])
{
// 交换元素
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
swapped = 1;
}
}

// 如果没有交换,数组已经有序
if (swapped == 0)
break;
}
}

void print_array(int arr[], int n)
{
for (int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}

int main(void)
{
int arr[] = {64, 34, 25, 12, 22, 11, 90};
int n = sizeof(arr) / sizeof(arr[0]);

printf("排序前:");
print_array(arr, n);

bubble_sort(arr, n);

printf("排序后:");
print_array(arr, n);

return 0;
}

示例 5:状态机实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include <stdio.h>

// 状态定义
enum State {
STATE_IDLE,
STATE_READING,
STATE_PROCESSING,
STATE_WRITING,
STATE_ERROR
};

int main(void)
{
enum State current_state = STATE_IDLE;
int input;

while (1)
{
switch (current_state)
{
case STATE_IDLE:
printf("当前状态:空闲\n");
printf("请输入命令 (1=开始读取, 2=退出):");
scanf("%d", &input);

if (input == 1)
{
current_state = STATE_READING;
}
else if (input == 2)
{
goto exit_program;
}
else
{
printf("无效命令\n");
}
break;

case STATE_READING:
printf("当前状态:读取数据\n");
// 模拟读取数据
printf("正在读取数据...\n");
current_state = STATE_PROCESSING;
break;

case STATE_PROCESSING:
printf("当前状态:处理数据\n");
// 模拟处理数据
printf("正在处理数据...\n");
current_state = STATE_WRITING;
break;

case STATE_WRITING:
printf("当前状态:写入数据\n");
// 模拟写入数据
printf("正在写入数据...\n");
current_state = STATE_IDLE;
break;

case STATE_ERROR:
printf("当前状态:错误\n");
// 处理错误
printf("正在处理错误...\n");
current_state = STATE_IDLE;
break;

default:
printf("无效状态\n");
current_state = STATE_IDLE;
break;
}

printf("\n");
}

exit_program:
printf("程序退出\n");
return 0;
}

控制语句的性能优化

条件分支预测

现代处理器使用分支预测器来预测条件分支的执行方向,提高执行效率。为了帮助处理器正确预测分支,可以:

  1. 保持分支的一致性:尽量使条件分支的执行方向保持一致
  2. 将最常见的情况放在前面:在 if-else 语句中,将最可能发生的情况放在前面
  3. 避免复杂的条件表达式:复杂的条件表达式会降低分支预测的准确性
  4. 使用查表代替条件分支:对于多个固定值的判断,可以使用查表代替 switch 或 if-else 语句

循环优化

  1. 减少循环次数:通过数学方法减少循环的总次数
  2. 循环不变量外提:将循环中不变的计算移到循环外部
  3. 循环展开:手动展开循环以减少循环开销
  4. 向量化:使用 SIMD 指令并行处理循环中的数据
  5. 减少内存访问:尽量使用寄存器变量,减少内存访问次数

跳转表的使用

对于多个固定值的判断,使用跳转表可以比 switch 语句更高效:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// 使用函数指针数组实现跳转表
void handle_case_0(void)
{
printf("处理情况 0\n");
}

void handle_case_1(void)
{
printf("处理情况 1\n");
}

void handle_case_2(void)
{
printf("处理情况 2\n");
}

void handle_default(void)
{
printf("处理默认情况\n");
}

// 函数指针数组
void (*jump_table[])(void) = {
handle_case_0,
handle_case_1,
handle_case_2
};

#define TABLE_SIZE (sizeof(jump_table) / sizeof(jump_table[0]))

void process_case(int case_num)
{
if (case_num >= 0 && case_num < TABLE_SIZE)
{
jump_table[case_num]();
}
else
{
handle_default();
}
}

代码风格和最佳实践

代码缩进和格式

  1. 缩进:使用 4 个空格或 1 个制表符进行缩进,保持一致
  2. 大括号:使用 K&R 风格或 Allman 风格,保持一致
  3. 行长度:每行代码不超过 80-100 个字符
  4. 空格:在运算符前后、逗号后使用空格,提高代码可读性
  5. 空行:在逻辑块之间使用空行,提高代码的可读性

注释的使用

  1. 函数注释:在函数定义前添加注释,说明函数的功能、参数、返回值
  2. 复杂代码:在复杂的代码段前添加注释,说明代码的逻辑
  3. 变量注释:对于重要的变量,添加注释说明其用途
  4. 避免过度注释:不要注释显而易见的代码

常见错误和避免方法

  1. 无限循环:确保循环条件能够在某个时刻变为假
  2. 缺少 break 语句:在 switch 语句中,确保每个 case 分支都有适当的 break 语句
  3. 空悬 else:使用大括号明确 else 的归属
  4. 整数溢出:注意循环变量的范围,避免整数溢出
  5. 缓冲区溢出:在处理数组和字符串时,注意边界检查
  6. 未初始化的变量:确保在使用变量前对其进行初始化
  7. 逻辑错误:仔细检查条件表达式的逻辑,避免逻辑错误
  8. 资源泄漏:在使用完资源后,确保正确释放

调试技巧

  1. 打印调试:在关键位置添加打印语句,输出变量的值
  2. 使用调试器:使用 gdb、lldb 等调试器进行调试
  3. 断言:使用 assert 宏检查程序的假设
  4. 单元测试:为关键功能编写单元测试
  5. 代码审查:通过代码审查发现潜在的问题

小结

本章详细介绍了 C 语言的控制语句,包括:

  • 条件语句if 语句、switch 语句及其变体和最佳实践
  • 循环语句while 循环、do-while 循环、for 循环及其嵌套和优化
  • 跳转语句break 语句、continue 语句、goto 语句的使用场景和注意事项
  • 控制语句的应用:实际应用示例、算法实现、状态机设计
  • 控制语句的性能优化:分支预测、循环优化、跳转表的使用
  • 代码风格和最佳实践:缩进、注释、常见错误和避免方法

控制语句是 C 语言编程的核心,掌握这些语句的使用方法和技巧对于编写高效、可靠的程序至关重要。通过合理使用控制语句,你可以实现各种复杂的算法和逻辑,解决实际问题。

在后续章节中,我们将学习函数、数组和指针等高级概念,这些概念将与控制语句结合使用,帮助你编写更加结构化、模块化和高效的代码。