C/C++ Linux 开发教程(GCC + CMake) / 03 — 控制流(if/for/while/switch)
控制流(if/for/while/switch)
1. if / else if / else
#include <stdio.h>
int main(void)
{
int score;
printf("请输入成绩: ");
scanf("%d", &score);
if (score >= 90) {
printf("优秀\n");
} else if (score >= 80) {
printf("良好\n");
} else if (score >= 60) {
printf("及格\n");
} else {
printf("不及格\n");
}
return 0;
}
⚠️ 注意: else 匹配最近的未匹配 if,不要被缩进迷惑。
2. 三元运算符
#include <stdio.h>
int main(void)
{
int a = 10, b = 20;
int max = (a > b) ? a : b;
printf("max = %d\n", max);
// 也可以嵌套(但不推荐过度嵌套)
int x = 5;
const char *label = (x > 0) ? "正数" : (x < 0) ? "负数" : "零";
printf("%d 是%s\n", x, label);
return 0;
}
💡 提示: 三元运算符适合简单的二选一赋值,复杂逻辑用
if/else更清晰。
3. for 循环
3.1 基本语法
for (初始化; 条件; 步进) {
循环体;
}
#include <stdio.h>
int main(void)
{
// 打印 1 到 10
for (int i = 1; i <= 10; i++) {
printf("%d ", i);
}
printf("\n");
// 倒序遍历
for (int i = 10; i >= 1; i--) {
printf("%d ", i);
}
printf("\n");
// 步进为 2
for (int i = 0; i <= 20; i += 2) {
printf("%d ", i);
}
printf("\n");
return 0;
}
3.2 for 循环变体
// 多变量控制
for (int i = 0, j = 10; i < j; i++, j--) {
printf("i=%d, j=%d\n", i, j);
}
// 无限循环
for (;;) {
// 用 break 退出
break;
}
// 省略部分
int i = 0;
for (; i < 10; ) {
printf("%d\n", i++);
}
4. while 循环
#include <stdio.h>
int main(void)
{
// while 循环
int n = 1;
while (n <= 100) {
n *= 2;
}
printf("第一个超过 100 的 2 的幂: %d\n", n);
// 读取输入直到 EOF
int val;
printf("输入整数(非数字退出): ");
while (scanf("%d", &val) == 1) {
printf("你输入了: %d\n", val);
}
return 0;
}
4.1 do-while 循环
do-while 至少执行一次循环体:
#include <stdio.h>
int main(void)
{
int n;
do {
printf("输入 1-10 的数字: ");
scanf("%d", &n);
} while (n < 1 || n > 10);
printf("你输入了: %d\n", n);
return 0;
}
💡 提示:
do-while适合"先执行再判断"的场景,如用户输入验证。
5. break / continue / goto
5.1 break
立即跳出当前循环:
#include <stdio.h>
int main(void)
{
// 查找第一个能被 7 整除的数
for (int i = 1; i <= 100; i++) {
if (i % 7 == 0) {
printf("找到: %d\n", i);
break;
}
}
return 0;
}
5.2 continue
跳过本次迭代,进入下一次:
#include <stdio.h>
int main(void)
{
// 打印 1-20 中所有奇数
for (int i = 1; i <= 20; i++) {
if (i % 2 == 0) continue;
printf("%d ", i);
}
printf("\n");
return 0;
}
5.3 goto
goto 可以跳转到函数内任意标签。过度使用会导致代码难以理解,但在某些场景下有其价值:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int *a = malloc(10 * sizeof(int));
if (!a) goto fail;
int *b = malloc(20 * sizeof(int));
if (!b) goto free_a;
int *c = malloc(30 * sizeof(int));
if (!c) goto free_b;
// 使用 a, b, c ...
printf("所有资源分配成功\n");
free(c);
free_b:
free(b);
free_a:
free(a);
fail:
return 0;
}
⚠️ 注意: goto 只能在同一函数内跳转,不能跨函数。仅在资源清理等必要场景使用。
6. switch / case / break / default
#include <stdio.h>
int main(void)
{
int day;
printf("输入星期几 (1-7): ");
scanf("%d", &day);
switch (day) {
case 1:
printf("星期一\n");
break;
case 2:
printf("星期二\n");
break;
case 3:
printf("星期三\n");
break;
case 4:
printf("星期四\n");
break;
case 5:
printf("星期五\n");
break;
case 6:
printf("星期六\n");
break;
case 7:
printf("星期日\n");
break;
default:
printf("无效输入\n");
break;
}
return 0;
}
6.1 Fall-through 特性
没有 break 时会"穿透"到下一个 case:
#include <stdio.h>
int main(void)
{
int month;
printf("输入月份 (1-12): ");
scanf("%d", &month);
int days;
switch (month) {
case 2:
days = 28; // 不考虑闰年
break;
case 4: case 6: case 9: case 11:
days = 30;
break;
case 1: case 3: case 5: case 7:
case 8: case 10: case 12:
days = 31;
break;
default:
printf("无效月份\n");
return 1;
}
printf("%d 月有 %d 天\n", month, days);
return 0;
}
💡 提示:
switch中case后面只能跟整型常量表达式,不能用变量或浮点数。
7. 嵌套循环
#include <stdio.h>
int main(void)
{
// 打印九九乘法表
for (int i = 1; i <= 9; i++) {
for (int j = 1; j <= i; j++) {
printf("%d×%d=%-4d", j, i, i * j);
}
printf("\n");
}
return 0;
}
输出:
1×1=1
1×2=2 2×2=4
1×3=3 2×3=6 3×3=9
...
嵌套循环中的 break
break 只跳出最内层循环:
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
if (j == 5) break; // 只跳出内层 for
printf("(%d,%d) ", i, j);
}
printf("\n");
}
8. 循环优化技巧
8.1 减少循环内计算
// 差:每次迭代都调用 strlen
for (int i = 0; i < strlen(str); i++) {
// ...
}
// 好:提前计算
int len = strlen(str);
for (int i = 0; i < len; i++) {
// ...
}
8.2 提前退出
// 搜索元素,找到即退出
int found = 0;
for (int i = 0; i < n; i++) {
if (arr[i] == target) {
found = 1;
break; // 不需要继续遍历
}
}
8.3 循环展开
// 每次迭代处理 4 个元素
int sum = 0;
int i;
for (i = 0; i + 3 < n; i += 4) {
sum += arr[i] + arr[i+1] + arr[i+2] + arr[i+3];
}
for (; i < n; i++) {
sum += arr[i];
}
💡 提示: 现代编译器在
-O2下通常会自动展开循环,手动展开主要用于热点代码。
9. 常见陷阱
9.1 无限循环
// 陷阱 1: 分号终止了 for 循环体
for (int i = 0; i < 10; i++); // 注意这里的分号!
{
printf("%d\n", i); // 这行不在循环内
}
// 陷阱 2: 死循环
int i = 0;
while (i < 10) {
printf("%d\n", i);
// 忘记 i++,永远满足 i < 10
}
// 陷阱 3: 有符号整数导致的无限循环
for (unsigned int i = 10; i >= 0; i--) { // 永远成立!
// unsigned 的 0 - 1 会回绕到 UINT_MAX
}
9.2 数组越界
int arr[5] = {1, 2, 3, 4, 5};
// 陷阱:<= 导致越界访问
for (int i = 0; i <= 5; i++) { // arr[5] 越界!
printf("%d\n", arr[i]);
}
9.3 整数溢出
// 陷阱:sum 可能溢出
int sum = 0;
for (int i = 1; i <= 100000; i++) {
sum += i * i; // 可能溢出 int 范围
}
// 使用 long long 避免
long long safe_sum = 0;
for (int i = 1; i <= 100000; i++) {
safe_sum += (long long)i * i;
}
10. 实际场景:猜数字游戏
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(void)
{
srand((unsigned)time(NULL));
int secret = rand() % 100 + 1;
int guess, attempts = 0;
printf("猜数字游戏:请猜一个 1-100 的数字\n");
do {
printf("你的猜测: ");
if (scanf("%d", &guess) != 1) break;
attempts++;
if (guess > secret) {
printf("太大了!\n");
} else if (guess < secret) {
printf("太小了!\n");
} else {
printf("恭喜!你猜中了!用了 %d 次\n", attempts);
}
} while (guess != secret && attempts < 10);
if (attempts >= 10 && guess != secret) {
printf("超过次数限制,答案是 %d\n", secret);
}
return 0;
}
编译运行:
gcc -Wall -std=c17 -o guess guess.c && ./guess
11. 实际场景:统计字符频率
#include <stdio.h>
#include <string.h>
int main(void)
{
const char *text = "hello world, hello c programming";
int freq[26] = {0};
for (int i = 0; text[i] != '\0'; i++) {
char ch = text[i];
if (ch >= 'a' && ch <= 'z') {
freq[ch - 'a']++;
}
}
printf("字符频率统计:\n");
for (int i = 0; i < 26; i++) {
if (freq[i] > 0) {
printf("'%c': %d\n", 'a' + i, freq[i]);
}
}
return 0;
}