Day5语法:循环-分支语句

目录

  • 1. 语句
  • 2. C 程序结构
  • 3. 分支语句
    • 3.1 if 语句
      • 3.1.1 形式一:单分支 if
      • 3.1.2 形式二:if-else
      • 3.1.3 形式三:if-else if-else
      • 3.1.4 练习
      • 3.1.5 if 语句总结
    • 3.2 switch
      • 3.2.1 语法形式
      • 3.2.2 注意事项
      • 3.2.3 作业
  • 4. 循环语句
    • 4.1 简介
    • 4.2 goto 语句
    • 4.3 while 语句
    • 4.4 do-while 语句
    • 4.5 for 循环
    • 4.6 循环的嵌套
    • 4.7 break 和 continue

1. 语句

C 语言中,语句(指令)就是表达程序员某个操作的意图。语句是 C 程序的基本执行单元,它表示一个完整的操作,每一条语句都是以;结束。语句可以分为单语句复合语句

单语句常见形式:

  • 赋值语句:int a = 10;
  • 函数调用语句:printf("hello world\n");
  • 空语句:;,什么都不做

复合语句是由一对花括号{}括起来的语句块(代码块),可以包含多个语句:

{ a = 22; b = a + 13; c = a + b; // ...... }

常见的流程控制语句包括ifswitchwhiledo whilefor等。C 程序主体由函数组成,函数内部由 C 语句组成。

2. C 程序结构

结构化程序设计提供三种控制结构:

  • 顺序结构:所有语句按照线性顺序执行。
  • 分支结构(选择结构):例如在 A、B 之间做出选择。
  • 循环结构:重复执行指定的语句。

早在 1966 年,就已经证明以上三种基本结构可以构成任意复杂的算法。

3. 分支语句

分支结构(选择结构)用于在多个分支中做出选择。C 语言中的分支语句主要包括if语句和switch语句。

3.1 if 语句

if 语句用于条件判断,满足条件时,就执行指定的语句。if 语句有三种基本形式。

3.1.1 形式一:单分支 if
if(表达式) 语句

如果“表达式”为真(非 0),表示条件成立,则执行后面的“语句”。合法的表达式就一定会有值。常见的表达式有:

  • 算术表达式:a + b
  • 逻辑表达式:a > b
  • 函数调用:sum(a, b)

if 条件成立时,表达式的值为非 0;if 条件不成立时,表达式的值为 0。

分析如下程序的结果:

示例 1:

int a = 3, b = 6; if(a > b) a = 4; // a > b 结果为假,此句没有执行 b = 3; // 执行 printf("a:%d b:%d \n", a, b); // 输出:a:3 b:3

规则:if 语句的作用域仅限于其后面的单条语句。如果希望多条语句被 if 管理,可以使用{}指定:

if(a > b) { a = 4; b = 3; }

示例 2:

int a = 4, b = 3; if(a < b); // 此处有分号 ; 代表一个空语句 a = 5; printf("a: %d\n", a);

编程建议:为了避免上述混乱情况,提高程序可读性,建议不管 if 后面有没有语句,都先加上{}确定范围:

if(xxx) { // 语句块; }
3.1.2 形式二:if-else
if(条件1) { 语句1 } else { 语句2 }

假设条件 1 成立(非 0),则执行对应的语句 1;否则执行语句 2。else 不能单独存在,一定要和一个 if 匹配。

练习:从键盘获取用户输入的一个整数,利用 if-else 语句,判断其是否为偶数。

if(n % 2 == 0) { printf("偶数\n"); } else { printf("奇数\n"); }
3.1.3 形式三:if-else if-else
if(条件1) { 语句1 } else if(条件2) { 语句2 } else if(条件3) { 语句3 } else { // 如果上述条件都不满足,则执行此处 }

这种结构适用于需要按优先级顺序逐个检查的情况。在每个条件后面跟随一个 else if,只有在前一个条件为假时,才会继续检查下一个条件;如果所有的表达式都不成立,则进入 else。

示例:根据学生成绩 score 打出对应的等级分。

if(score >= 90) { printf("A\n"); } else if(score >= 80) { printf("B\n"); } else if(score >= 60) { printf("C\n"); } else { printf("D\n"); }

用户输入 90 分时,仅输出 A。各个条件按照优先级查找,满足条件时执行对应的代码块。

区别于以下写法:

if(score >= 90) { printf("A\n"); } if(score >= 80) { printf("B\n"); } if(score >= 60) { printf("C\n"); } else { printf("D\n"); }

用户输入 90 分时,A、B、C 都会输出。

3.1.4 if 语句总结

1. 并列条件与互斥条件:

// 并列条件 if(条件1) { ... } if(条件2) { ... } if(条件3) { ... } // 互斥条件 if(条件1) { ... } else { ... }

2. if 语句的嵌套:在某个大的条件下再细分条件。在 if 语句中包含一个或者多个 if,称之为 if 嵌套。

if(A) { if(C) { } else if(D) { } else { } } else { }

注意 else 匹配原则:else 总是与它上面最近的、同级的、未配对的 if 语句匹配。

练习:判断从键盘输入的内容是否为字母(A~Z、a~z),如果是小写字母则输出其大写,如果是大写字母则输出其小写。

提示:

  • ASCII 码(虚拟机终端输入man ascii查看,按 q 退出)。
  • scanf("%c", &ch);用于获取输入。
// 获取用户输入,判断是否为字母 char ch; printf("请输入一个字母: ...\n"); scanf("%c", &ch); if((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')) { printf("输入的字符是字母\n"); // 进一步判断是大写还是小写 if(ch >= 'A' && ch <= 'Z') // 大写 { ch = ch + ('a' - 'A'); // 大写转小写 printf("转换为小写: %c\n", ch); } else if(ch >= 'a' && ch <= 'z') { ch = ch - ('a' - 'A'); // 小写转大写 printf("转换为大写: %c\n", ch); } } else { printf("输入的字符不是字母\n"); }

3.2 switch

switch 用来列举表达式所有可能的情况并进行匹配。

3.2.1 语法形式
switch(表达式) { case 常量表达式1: 语句1 break; // 跳出 case 常量表达式2: 语句2 break; case 常量表达式m: 语句m break; default: // 建议将 default 写在最后 语句 break; }

常量表达式是程序运行期间值不会改变的表达式。例如,下面的 case 写法都是正确的:

#define N 122 switch(num) { case N: // define 在预处理阶段替换,相当于 case 122: case 5: case 'A': case 'z': case 3 + 5: case 'a' + 32: break; }

错误示例:

int a = 3; switch(num) { case a + 1: // 错误:case 后不能使用变量 break; }

switch 让“表达式”和 case 后面的“常量表达式”做匹配。一旦匹配成功(值相等),则从那个 case 后面开始执行,直到遇到 break。如果所有的 case 都没有匹配成功,则匹配 default。

3.2.2 注意事项

1. switch 后面的“表达式”必须是一个可以列举的值,表达式的类型必须是整数值:整型 int、字符型 char、枚举。

int num; switch(num) { // ... }

2. case 后面的“常量表达式”也必须是一个整数值,不能是变量,并且每个 case 后面的常量表达式的值必须不同。

// 正确写法 switch(num) { case 1: break; case 2: break; } // 错误写法:case 1 出现了两次 switch(num) { case 1: // AAAAA break; case 1: // BBBBB break; }

3. 各个 case 和 default 出现的次序不影响匹配结果。一旦匹配成功,就从此处往下执行,直到遇到 break 或者 switch 语句结束。

int a = 5; switch(a) { // default 是所有 case 都不匹配时才会执行 default: ... case 1: ... case 2: ... case 5: a = 20; // 匹配此处并执行 break; // 此处跳出 case 6: ... }

4. “语句”可以省略,多个 case 可以共用一组语句:

switch(a) { case 1: case 2: // a 为 1 或者 2 时都执行此处 ... case 3: ... }

5. switch 中的 break 用来跳出它所属的 switch 语句:

switch(a) { case 1: case 2: ... case 3: break; default: ... }

4. 循环语句

先看一个场景:通过程序实现求 1+2+3+4+5+...+n 的和。

int sum = 0; // 用于保存累加的值 int n = 1; // 要加的数 sum = sum + n; // 0+1 n++; // 1-->2 sum = sum + n; // 1+2 n++; // ...... // 如果求到 n,就需要重复执行 n 次 // ----> 循环

4.1 简介

循环的本质是重复。所有重复的代码,都可以用循环来实现。

在 C 程序中,能够起到循环效果的语句如下:

  • ifgoto构造循环
  • while语句
  • do while语句
  • for语句

其中ifgoto并不算严格意义上的循环语句,只是起到循环效果。

4.2 goto 语句

goto语句可以让 CPU 跳转到指定位置执行,是一种无条件跳转语句。

goto 行标识;

“行标识”用于标识一行代码,需要符合 C 语言标识符的命名规则:由字母、数字、下划线组成,不能以数字开头,不能和关键字冲突。本质上它就代表了一个地址。

loop: sum = sum + n; n++; goto loop;

通过 goto 实现十次循环,打印 1-10:

int main() { int n = 1; loop: printf("loop: n %d\n", n); n++; if(n <= 10) { goto loop; } printf("loop end\n"); return 0; }

注意:goto 类似于汇编代码的无条件跳转,通常被认为是不好的编程风格,会让代码难以调试和阅读,因此平时尽量不要使用 goto 来做循环。

4.3 while 语句

while(表达式) { // 语句块 }

当“表达式”的值为真(非 0)时,执行“语句块”,然后跳回 while 处继续判断,如此重复,直到“表达式”的值为假(0)为止。

“表达式”可以是任意合法的 C 表达式,只要有值。“语句块”是循环体,是重复执行的部分,可以是单语句,也可以是复合语句。

int sum = 0; int i = 1; while(i <= 100) sum += i; i++; // 此句不会参与循环

编程建议:不管 while 后面有没有语句,都先加上大括号{}确定范围:

while(xxx) { // 要循环的内容 }

练习 1:逆序输出一个 3 位正整数。输入 123,输出 321。

int main() { int a = 0; // 用于获取用户输入的数 int d = 0; // 用于存储每一个位 printf("请输入一个三位的正整数\n"); scanf("%d", &a); // a 不为 0 则继续循环 while(a) { d = a % 10; // 取出末位 printf("%d", d); // 打印末位 a = a / 10; // 处理剩余位数 } return 0; }

练习 2:求 100 以内 3 的倍数的和。

int main() { int n = 1; // 表示 100 以内的某个数 int sum = 0; // 用于累计它们的和 while(n <= 100) { if(n % 3 == 0) { sum += n; } n++; } printf("sum :%d\n", sum); return 0; }

4.4 do-while 语句

do // 语句 while(表达式);

程序先执行“语句”,然后再判断“表达式”的值。当值为真(非 0)时,跳转到上面继续执行“语句”,如此重复,直到“表达式”的值为假(0)为止。

翻译成 goto 语句:

loop: 语句 t = 表达式; if(t) { goto loop; }

示例:

int i = 0; int sum = 0; do i++; sum += i; while(i < 100); printf("sum %d\n", sum);

如果 do 后面不加花括号,上述代码会报错:error: expected ‘while’ before ‘sum’。应改成:

int i = 0; int sum = 0; do { i++; sum += i; } while(i < 100); printf("sum %d\n", sum);

编程建议:使用 do-while 时,先给 do 后打一对{}确定范围:

do { } while();

while 和 do-while 的区别主要在于条件判断的时机:while 在每次循环开始前判断,do-while 在每次循环结束后判断。如果希望至少执行一次循环,可以使用 do-while;如果希望每次开始前先判断,可以使用 while。

特殊示例 do while(0):遇到复杂逻辑需要分步提前退出时,可以尝试使用 do-while(0)。

int a = 0, b = 0, c = 0; // 用于存储边长 scanf("%d%d%d", &a, &b, &c); do { if(a + b < c) { printf("no\n"); break; } if(a + c < b) { printf("no\n"); break; } if(b + c < a) { printf("no\n"); break; } // 当上述任意一个条件不满足时,会提前 break 退出 printf("最终: 满足三角形要求\n"); } while(0);

4.5 for 循环

for(initialization; continuation; action) { statement; }

其中 initialization 是初始化表达式,用于初始化循环变量,只执行一次;continuation 是判断表达式,只要为真就会不断执行循环体;action 是循环变量处理表达式,每轮循环结束后执行,使得循环变量产生变化。

更常见的形式:

for(表达式1; 表达式2; 表达式3) { 语句 }

先执行一次表达式 1,然后判断表达式 2 的值;如果为真,执行语句,再执行表达式 3,如此重复,直到表达式 2 为假。

三个表达式都可以省略,但两个分号不能省略:

for( ; ; ) { // 如果表达式 2 省略,表示 for 循环的执行条件永远为真 }

for 和 while 的区别:一般来说 while 更注重循环条件,for 更注重循环次数,两者可以互相替换。

练习:求“水仙花数”。水仙花数是三位数(100-999),且个位、十位、百位的立方和等于其本身。

int x = 100; int a, b, c; // 表示 x 的每一位 for(x = 100; x <= 999; x++) { a = x % 10; // 个位 b = (x / 10) % 10; // 十位 c = x / 100 % 10; // 百位 if((a * a * a + b * b * b + c * c * c) == x) { printf("找到水仙花数: %d\n", x); } }

附:C 中使用pow做幂运算:

double pow(double x, double y); int a = 3; // 例如求 3 的 4 次方 printf("%.0f\n", pow((double)a, 4.0)); // 在 Ubuntu 中使用 gcc 编译时需要连接 -lm // gcc 1.c -lm

4.6 循环的嵌套

示例:打印 3 行 6 列全为 0 的矩阵。

for(int j = 0; j < 3; j++) { for(int i = 0; i < 6; i++) { printf("0 "); } printf("\n"); }

4.7 break 和 continue

break跳出当前的循环或 switch 语句;continue结束本次循环,继续下一次循环。

示例 1:continue

int i = 10; int sum = 0; while(i--) { if(i > 5) { continue; sum += i; // 不会执行到此处 } else { sum += i; } } printf("sum %d\n", sum); // 5+4+3+2+1

示例 2:break

int i = 10; int sum = 0; while(i--) { if(i > 5) break; // 直接跳出整个 while 循环 else sum += i; } printf("sum %d\n", sum); // 0