编程进阶网编程进阶网
  • 基础组成体系
  • 程序编程原理
  • 异常和IO系统
  • 六大设计原则
  • 设计模式导读
  • 创建型设计模式
  • 结构型设计模式
  • 行为型设计模式
  • 设计模式案例
  • 面向对象思想
  • 基础入门
  • 高级进阶
  • JVM虚拟机
  • 数据集合
  • Java面试题
  • C语言入门
  • C综合案例
  • C标准库
  • C语言专栏
  • C++入门
  • C++综合案例
  • C++专栏
  • HTML
  • CSS
  • JavaScript
  • 前端专栏
  • Swift
  • iOS入门
  • 基础入门
  • 开源库解读
  • 性能优化
  • Framework
  • 方案设计
  • 媒体音视频
  • 硬件开发
  • Groovy
  • 常用工具
  • 大厂面试题
  • 综合案例
  • 网络底层
  • Https
  • 网络请求
  • 故障排查
  • 专栏
  • 数组
  • 链表
  • 栈
  • 队列
  • 树
  • 递归
  • 哈希
  • 排序
  • 查找
  • 字符串
  • 其他
  • Bash脚本
  • Linux入门
  • 嵌入式开发
  • 代码规范
  • Markdown
  • 开发理论
  • 开发工具
  • Git管理
  • 百宝箱
  • 开源协议
  • 技术招聘
  • 测试经验
  • 职场提升
  • 技术模版
  • 关于我
  • 目标清单
  • 学习框架
  • 育儿经验
  • 我的专栏
  • 底层能力
  • 读书心得
  • 随笔笔记
  • 职场思考
  • 中华历史
  • 经济学故事
  • 基础组成体系
  • 程序编程原理
  • 异常和IO系统
  • 六大设计原则
  • 设计模式导读
  • 创建型设计模式
  • 结构型设计模式
  • 行为型设计模式
  • 设计模式案例
  • 面向对象思想
  • 基础入门
  • 高级进阶
  • JVM虚拟机
  • 数据集合
  • Java面试题
  • C语言入门
  • C综合案例
  • C标准库
  • C语言专栏
  • C++入门
  • C++综合案例
  • C++专栏
  • HTML
  • CSS
  • JavaScript
  • 前端专栏
  • Swift
  • iOS入门
  • 基础入门
  • 开源库解读
  • 性能优化
  • Framework
  • 方案设计
  • 媒体音视频
  • 硬件开发
  • Groovy
  • 常用工具
  • 大厂面试题
  • 综合案例
  • 网络底层
  • Https
  • 网络请求
  • 故障排查
  • 专栏
  • 数组
  • 链表
  • 栈
  • 队列
  • 树
  • 递归
  • 哈希
  • 排序
  • 查找
  • 字符串
  • 其他
  • Bash脚本
  • Linux入门
  • 嵌入式开发
  • 代码规范
  • Markdown
  • 开发理论
  • 开发工具
  • Git管理
  • 百宝箱
  • 开源协议
  • 技术招聘
  • 测试经验
  • 职场提升
  • 技术模版
  • 关于我
  • 目标清单
  • 学习框架
  • 育儿经验
  • 我的专栏
  • 底层能力
  • 读书心得
  • 随笔笔记
  • 职场思考
  • 中华历史
  • 经济学故事
  • 01.基础语法
  • 02.变量和数据类型
  • 03.循环和选择
  • 04.输入和输出
  • 05.数组和容器
  • 06.函数和返回值
  • 07.结构体
  • 08.指针
  • 09.预处理
  • 10.文件操作
  • 11.应用调试

03.循环和选择

目录介绍

  • 3.1 语句和程序块
  • 3.2 选择结构
    • 3.2.1 if语句
    • 3.2.2 单行格式if语句
    • 3.2.3 多行格式if语句
    • 3.2.3 多行格式if语句
    • 3.2.4 多条件的if语句
    • 3.2.5 嵌套if语句
    • 3.2.6 三目运算符
    • 3.2.7 switch语句
  • 3.3 循环结构
    • 3.3.1 while循环语句
    • 3.3.2 do...while循环
    • 3.3.3 for循环语句
  • 3.4 跳出循环
    • 3.4.1 break语句
    • 3.4.2 continue语句
    • 3.4.3 goto语句
  • 3.5 课后的习题

3.1 语句和程序块

C 语言的程序是顺序执行,即先执行前面的语句,再执行后面的语句。开发者如果想要控制程序执行的流程,就必须使用流程控制的语法结构,主要是条件执行和循环执行。

C/C++支持最基本的三种程序运行结构:==顺序结构、选择结构、循环结构==

  • 顺序结构:程序按顺序执行,不发生跳转
  • 选择结构:依据条件是否满足,有选择的执行相应功能
  • 循环结构:依据条件是否满足,循环多次执行某段代码

3.2 选择结构

3.2.1 if语句

**作用:**执行满足条件的语句

if语句的三种形式

  • 单行格式if语句

  • 多行格式if语句

  • 多条件的if语句

3.2.2 单行格式if语句

if语句用于条件判断,满足条件时,就执行指定的语句。

if (expression) statement

上面式子中,表达式expression为真(值不为0)时,就执行statement语句。

示例中,当变量x为10时,就会输出一行文字。对于只有一个语句的语句体,语句部分通常另起一行。

if (x == 10)
  printf("x is 10\n");

如果有多条语句,就需要把它们放在大括号里面,组成一个复合语句。

if (line_num == MAX_LINES) {
  line_num = 0;
  page_num++;
}

3.2.3 多行格式if语句

if语句可以带有else分支,指定条件不成立时(表达式expression的值为0),所要执行的代码。

if (expression) statement
else statement

下面是一个例子。

if (i > j)
  max = i;
else
  max = j;

3.2.4 多条件的if语句

如果else的语句部分多于一行,同样可以把它们放在大括号里面。else可以与另一个if语句连用,构成多重判断。

if (expression)
  statement
else if (expression)
  statement
...
else if (expression)
  statement
else
  statement

3.2.5 嵌套if语句

如果有多个if和else,可以记住这样一条规则,else总是跟最接近的if匹配。

if (number > 6)
  if (number < 12)
    printf("The number is more than 6, less than 12.\n");
else
  printf("It is wrong number.\n");

上面示例中,else部分匹配最近的if(即number < 12),所以如果number等于6,就不会执行else的部分。

这样很容易出错,为了提供代码的可读性,建议使用大括号,明确else匹配哪一个if。

if (number > 6) {
  if (number < 12) {
    printf("The number is more than 6, less than 12.\n");
  }
} else {
  printf("It is wrong number.\n");
}

上面示例中,使用了大括号,就可以清晰地看出else匹配外层的if。

3.2.6 三目运算符

C 语言有一个三元表达式?:,可以用作if...else的简写形式。

<expression1> ? <expression2> : <expression3>

这个操作符的含义是,表达式expression1如果为true(非0值),就执行expression2,否则执行expression3。

下面是一个例子,返回两个值之中的较大值。

(i > j) ? i : j;

上面的代码等同于下面的if语句。

if (i > j)
  return i;
else
  return j;

3.2.7 switch语句

switch 语句是一种特殊形式的 if...else 结构,用于判断条件有多个结果的情况。它把多重的else if改成更易用、可读性更好的形式。

switch (expression) {
  case value1: statement
  case value2: statement
  default: statement
}

上面代码中,根据表达式expression不同的值,执行相应的case分支。如果找不到对应的值,就执行default分支。

下面是一个例子。

switch (grade) {
  case 0:
    printf("False");
    break;
  case 1:
    printf("True");
    break;
  default:
    printf("Illegal");
}

上面示例中,根据变量grade不同的值,会执行不同的case分支。如果等于0,执行case 0的部分;如果等于1,执行case 1的部分;否则,执行default的部分。default表示处理以上所有case都不匹配的情况。

每个case语句体的结尾,都应该有一个break语句,作用是跳出整个switch结构,不再往下执行。如果缺少break,就会导致继续执行下一个case或default分支。

switch (grade) {
  case 0:
    printf("False");
  case 1:
    printf("True");
    break;
  default:
    printf("Illegal");
}

上面示例中,case 0的部分没有break语句,导致这个分支执行完以后,不会跳出switch结构,继续执行case 1分支。

利用这个特点,如果多个case分支对应同样的语句体,可以写成下面这样。

switch (grade) {
  case 0:
  case 1:
    printf("True");
    break;
  default:
    printf("Illegal");
}

上面示例中,case 0分支没有任何语句,导致case 0和case 1都会执行同样的语句体。

case后面的语句体,不用放在大括号里面,这也是为什么需要break的原因。

default分支用来处理前面的 case 都不匹配的情况,最好放在所有 case 的后面,这样就不用写break语句。这个分支是可选的,如果没有该分支,遇到所有的 case 都不匹配的情况,就会直接跳出整个 switch 代码块。

3.3 循环结构

3.3.1 while循环语句

while语句用于循环结构,满足条件时,不断执行循环体。

while (expression)
  statement

上面代码中,如果表达式expression为非零值(表示真),就会执行statement语句,然后再次判断expression是否为零;如果expression为零(表示伪)就跳出循环,不再执行循环体。

while (i < n)
  i = i + 2;

上面示例中,只要i小于n,i就会不断增加2。

如果循环体有多个语句,就需要使用大括号将这些语句组合在一起。

while (expression) {
  statement;
  statement;
}

下面是一个例子。

i = 0;

while (i < 10) {
  printf("i is now %d!\n", i);
  i++;
}

printf("All done!\n");

上面代码中,循环体会执行10次,每次将i增加1,直到等于10才退出循环。

只要条件为真,while会产生无限循环。下面是一种常见的无限循环的写法。

while (1) {
  // ...
}

上面的示例虽然是无限循环,但是循环体内部可以用break语句跳出循环。

3.3.2 do...while循环

do...while结构是while的变体,它会先执行一次循环体,然后再判断是否满足条件。如果满足的话,就继续执行循环体,否则跳出循环。

do statement
while (expression);

上面代码中,不管条件expression是否成立,循环体statement至少会执行一次。每次statement执行完毕,就会判断一次expression,决定是否结束循环。

i = 10;

do --i;
while (i > 0);

上面示例中,变量i先减去1,再判断是否大于0。如果大于0,就继续减去1,直到i等于0为止。

如果循环部分有多条语句,就需要放在大括号里面。

i = 10;

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

printf("All done!\n");

上面例子中,变量i并不满足小于10的条件,但是循环体还是会执行一次。

3.3.3 for循环语句

for语句是最常用的循环结构,通常用于精确控制循环次数。

for (initialization; continuation; action)
  statement;

上面代码中,for语句的条件部分(即圆括号里面的部分)有三个表达式。

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

循环体部分的statement可以是一条语句,也可以是放在大括号里面的复合语句。下面是一个例子。

for (int i = 10; i > 0; i--)
  printf("i is %d\n", i);

上面示例中,循环变量i在for的第一个表达式里面声明,该变量只用于本次循环。离开循环体之后,就会失效。

条件部分的三个表达式,每一个都可以有多个语句,语句与语句之间使用逗号分隔。

int i, j;
for (i = 0, j = 999; i < 10; i++, j--) {
  printf("%d, %d\n", i, j);
}

上面示例中,初始化部分有两个语句,分别对变量i和j进行赋值。

for的三个表达式都不是必需的,甚至可以全部省略,这会形成无限循环。

for (;;) {
  printf("本行会无限循环地打印。\n" );
}

上面示例由于没有判断条件,就会形成无限循环。

3.4 跳出循环

3.4.1 break语句

break语句有两种用法。一种是与switch语句配套使用,用来中断某个分支的执行,这种用法前面已经介绍过了。另一种用法是在循环体内部跳出循环,不再进行后面的循环了。

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

上面示例中,break语句使得循环跳到下一个i。

while ((ch = getchar()) != EOF) {
  if (ch == '\n') break;
  putchar(ch);
}

上面示例中,一旦读到换行符(\n),break命令就跳出整个while循环,不再继续读取了。

注意,break命令只能跳出循环体和switch结构,不能跳出if结构。

if (n > 1) {
  if (n > 2) break; // 无效
  printf("hello\n");
}

上面示例中,break语句是无效的,因为它不能跳出外层的if结构。

3.4.2 continue语句

continue语句用于在循环体内部终止本轮循环,进入下一轮循环。只要遇到continue语句,循环体内部后面的语句就不执行了,回到循环体的头部,开始执行下一轮循环。

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

上面示例中,有没有continue语句,效果一样,都表示跳到下一个j。

while ((ch = getchar()) != '\n') {
  if (ch == '\t') continue;
  putchar(ch);
}

上面示例中,只要读到的字符是制表符(\t),就用continue语句跳过该字符,读取下一个字符。

3.4.3 goto语句

goto 语句用于跳到指定的标签名。这会破坏结构化编程,建议不要轻易使用,这里为了语法的完整,介绍一下它的用法。

char ch;

top: ch = getchar();

if (ch == 'q')
  goto top;

上面示例中,top是一个标签名,可以放在正常语句的前面,相当于为这行语句做了一个标记。程序执行到goto语句,就会跳转到它指定的标签名。

infinite_loop:
  print("Hello, world!\n");
  goto infinite_loop;

上面的代码会产生无限循环。

goto 的一个主要用法是跳出多层循环。

for(...) {
  for (...) {
    while (...) {
      do {
        if (some_error_condition)
          goto bail;    
      } while(...);
    }
  }
}
    
bail:
// ... ...

上面代码有很复杂的嵌套循环,不使用 goto 的话,想要完全跳出所有循环,写起来很麻烦。

goto 的另一个用途是提早结束多重判断。

if (do_something() == ERR)
  goto error;
if (do_something2() == ERR)
  goto error;
if (do_something3() == ERR)
  goto error;
if (do_something4() == ERR)
  goto error;

上面示例有四个判断,只要有一个发现错误,就使用 goto 跳过后面的判断。

注意,goto 只能在同一个函数之中跳转,并不能跳转到其他函数。

3.5 课后的习题

贡献者: yangchong211
上一篇
02.变量和数据类型
下一篇
04.输入和输出