循环,分支,跳转【由浅入深.c】


前言

本文介绍循环,分支,跳转相关内容。

(【由浅入深】是一个系列文章,它记录了我个人作为一个小白,在学习c++技术开发方向计相关知识过程中的笔记,欢迎各位彭于晏刘亦菲从中指出我的错误并且与我共同学习进步,作为该系列的第一部曲-c语言,大部分知识会根据本人所学和我的助手——通义,DeepSeek,腾讯元宝等以及合并网络上所找到的相关资料进行核实誊抄,每一篇文章都可能会因为一些错误在后续时间增删改查,因为该系列按照我的网络课程学习笔记形式编写,我会使用绝大多数人使用的讲解顺序编写,所以基础框架和大部分内容案例会与他人一样,基础知识不会过于详细讲述)


一.while循环

  • 基本概念:C语言中的while循环是一种前测试循环结构,用于在特定条件满足时重复执行一段代码。它的工作原理是:先判断条件表达式,如果为真(非0),则执行循环体;执行完循环体后,再次判断条件,直到条件为假(0)时退出循环

  • 语法结构

     while (条件表达式) {
     // 循环体语句
     }
     条件表达式:可以是任何表达式,C语言中非0值被视为真,0值被视为假。
    循环体:可以是单个语句,也可以是用花括号`{}`括起来的语句块。
    
  • 工作原理

    1. 条件判断:程序首先评估条件表达式
    2. 条件为真:如果条件为真(非0),执行循环体
    3. 条件为假:如果条件为假(0),跳过循环体,执行循环后的代码
    4. 重复判断:执行完循环体后,再次回到条件判断,重复步骤1-3
  • 使用示例

    1. 简单计数示例
#include <stdio.h>
int main() {
    int i = 1;
    while (i <= 10) {
        printf("%d ", i);
        i++;
    }
    return 0;
}
输出:1 2 3 4 5 6 7 8 9 10
  1. 计算1-100的和
#include <stdio.h>
int main() {
    int i = 1;
    int sum = 0;
    while (i <= 100) {
        sum += i;
        i++;
    }
    printf("1到100的和是:%d\n", sum);
    return 0;
}
  1. 无限循环(需谨慎使用)
#include <stdio.h>
int main() {
    while (1) {  // 条件永远为真
        printf("无限循环中...\n");
        // 通常需要添加退出条件,否则程序将一直运行
    }
    return 0;
}
  • 关键注意事项

    1. 避免无限循环

      • 必须在循环体内包含修改条件的语句
      • 例如:i++i--等,确保条件最终会变为假
    2. 条件表达式优化

      • 推荐使用布尔表达式,避免复杂计算
      • 例如:while (i < 100)while (i <= 100 - 1) 更清晰
    3. 变量初始化

      • 循环条件中使用的变量必须在循环前初始化
      • 例如:int i = 1; 在while循环前
    4. 循环控制语句

      • break:立即终止循环,跳到循环之后的代码
      • continue:跳过本次循环剩余部分,直接进入下一次循环
  • 常见应用场景

    1. 未知次数的重复操作

      • 从文件中读取数据直到文件结束
      • 等待用户输入特定字符
      • 处理网络请求直到成功
    2. 遍历数据结构

      • 遍历数组或链表
      • 处理树形结构
    3. 条件满足时的重复操作

      • 计算直到达到特定精度
      • 检查条件直到满足
  • 死循环的处理:如果条件永远为真,程序会陷入无限循环。在实际开发中,应避免这种情况,或者添加退出条件:

#include <stdio.h>
int main() {
    int count = 0;
    while (1) {
        printf("循环中: %d\n", count);
        count++;
        if (count > 10) {
            break;  // 当计数达到10时退出循环
        }
    }
    return 0;
}

二.do-while循环

  • 基本概念:do-while循环是C语言中的一种后测试循环结构,它的特点是先执行循环体,再检查条件。这意味着无论条件是否满足,循环体至少会执行一次

  • 语法结构

    do {
    // 循环体语句
    } while (条件表达式);
    
  • 重要注意

    • while后的条件表达式后面必须加上分号(;),这是do-while循环的语法要求
    • 条件表达式可以是任何C语言表达式,非0值为真,0值为假
  • 工作原理

    1. 首先执行循环体:程序先执行do和while之间的代码块
    2. 然后检查条件:执行完循环体后,检查while后的条件表达式
    3. 决定是否继续:如果条件为真(非0),则再次执行循环体;如果条件为假(0),则退出循环
  • 与while循环的区别

    特性while循环do-while循环
    执行顺序先判断条件,再执行循环体先执行循环体,再判断条件
    执行次数条件为假时,循环体可能不执行至少执行一次循环体
    语法while(条件) {循环体}do {循环体} while(条件);
  • 与while循环的对比示例

    // while循环示例
     int i = 5;
    while (i < 5) {
    printf("%d ", i);
    i++;
    }
    // 这个循环体不会执行,因为条件一开始就不满足
    // do-while循环示例
    int i = 5;
    do {
        printf("%d ", i);
         i++;
     } while (i < 5);
    // 这个循环体执行一次,输出5,然后条件不满足,退出循环
    
  • 代码示例

    示例1:打印1-10的数字

     #include <stdio.h>
     int main() {
     int i = 1;
     do {
         printf("%d ", i);
         i++;
     } while (i <= 10);
     return 0;
    }
     //输出:1 2 3 4 5 6 7 8 9 10
    

    示例2:计算输入数字的位数
    特别说明:如果输入为0,do-while循环能正确输出1(因为0是一位数),而while循环需要额外处理。

    #include <stdio.h>
    int main() {
    int n;
    int count = 0;
    scanf("%d", &n);
    do {
        n /= 10;  // 去掉个位
        count++;
    } while (n);
    printf("%d", count);
    return 0;
    }
    

    示例3:输入验证(必须至少输入一次)

    #include <stdio.h>
    int main() {
    int number;
    do {
        printf("请输入一个正整数: ");
        scanf("%d", &number);
        if (number <= 0) {
            printf("输入错误,请重新输入!\n");
        }
     } while (number <= 0);
    printf("你输入的正整数是: %d\n", number);
    return 0;
    }
    
  • 为什么需要do-while循环?
    do-while循环的主要价值在于保证至少执行一次。在以下场景中特别有用:

    1. 输入验证:确保用户至少输入一次数据
    2. 计算数字位数:如示例2所示
    3. 菜单驱动程序:确保用户至少看到一次菜单
    4. 游戏循环:先执行游戏逻辑再判断是否继续
  • 常见错误

    1. 忘记分号while(条件表达式)后面必须加分号,这是语法要求
     // 错误示例
     do {
      printf("Hello");
     } while (i < 5) // 缺少分号
    
    1. 无限循环:如果条件表达式始终为真,会导致无限循环
    // 错误示例
    do {
        printf("Infinite loop");
    } while (1); // 条件永远为真
    

三.for循环

  • 基本概念
    for循环是C语言中最常用的循环结构之一,它将初始化、条件判断和更新操作集中在一个语句中,使代码更加简洁、可读性更强。for循环特别适合于已知迭代次数的场景,如遍历数组、执行固定次数的操作等。

  • 语法结构

     for (初始化表达式; 条件表达式; 更新表达式) {
     // 循环体
     }
    
    • 初始化表达式:在循环开始前执行一次,通常用于声明和初始化循环变量。
    • 条件表达式:每次迭代前都会进行判断,如果为真(非0),则执行循环体;如果为假(0),则终止循环。
    • 更新表达式:每次循环迭代结束后执行,用于更新循环变量。
  • 工作原理

    1. 执行初始化表达式:只在循环开始前执行一次
    2. 检查条件表达式:如果为真,执行循环体;如果为假,跳过循环体,执行循环后的代码
    3. 执行循环体:执行循环体中的代码
    4. 执行更新表达式:更新循环变量
    5. 返回步骤2:重新检查条件表达式,重复步骤2-4,直到条件为假
  • 详细示例

    1. 简单计数示例
     #include <stdio.h>
      int main() {
     for (int i = 1; i <= 10; i++) {
         printf("%d ", i);
     }
     return 0;
    }
    //输出:1 2 3 4 5 6 7 8 9 10
    
    1. 计算1-100的和
    #include <stdio.h>
    int main() {
    int sum = 0;
    for (int i = 1; i <= 100; i++) {
       sum += i;
    }
    printf("1到100的和是:%d\n", sum);
    return 0;
    }
    
    1. 遍历数组
    #include <stdio.h>
    int main() {
    int arr[] = {1, 2, 3, 4, 5};
    int size = sizeof(arr) / sizeof(int);
    
    for (int i = 0; i < size; i++) {
       printf("数组元素 %d: %d\n", i, arr[i]);
    }
    
    return 0;
    }
    
  • for循环的高级用法
    1. 省略部分表达式:for`循环的三个部分都可以省略,但需要谨慎处理:

// 省略初始化表达式(需在循环前声明变量)
int i = 0;
for (; i < 10; i++) {
    printf("%d ", i);
}

// 省略条件表达式(相当于while(1),需要在循环体内使用break)
for (int i = 0; ; i++) {
    if (i >= 10) break;
    printf("%d ", i);
}

// 省略更新表达式(需在循环体内更新变量)
for (int i = 0; i < 10;) {
    printf("%d ", i);
    i++;
}
  1. 无限循环
// 无限循环(需在循环体内使用break)
for (;;) {
    // 循环体
    if (some_condition) {
        break;
    }
}
  • 与while循环的比较

    特点while循环for循环
    适用场景条件未知或循环次数不确定已知循环次数
    代码结构仅需条件判断包含初始化、条件判断、更新
    可读性有时较难理解循环控制通常更清晰
    适用性适用于更复杂的条件判断适用于简单计数循环
  • C99/C11标准问题
    错误'for' loop initial declarations are only allowed in C99 or C11 mode
    原因:在C89/C90标准下,不允许在for循环初始化部分声明变量。
    解决方案

// 方案一:在循环前声明变量
int i;
for (i = 0; i < 10; i++) {
    printf("%d ", i);
}

// 方案二:使用C99或C11标准编译
// 例如:gcc -std=c99 filename.c
  • for循环与while循环的等价转换
// for循环
for (int i = 0; i < 10; i++) {
    // 循环体
}

// 等价的while循环
int i = 0;
while (i < 10) {
    // 循环体
    i++;
}
  • 与C99/C11标准相关
    在C99及以后的标准中,可以在for循环的初始化部分声明变量,这使代码更加简洁:
// C99/C11标准支持
for (int i = 0; i < 10; i++) {
    printf("%d ", i);
}

在C89/C90标准中,需要在循环前声明变量:

// C89/C90标准
int i;
for (i = 0; i < 10; i++) {
    printf("%d ", i);
}

四.if语句

  • 基本概念:if语句是C语言中最基本的条件控制结构,用于根据特定条件判断执行不同的代码路径。在C语言中,条件表达式的值为非零(true)时,执行相应的代码块;值为0(false)时,跳过该代码块。

  • 语法结构
    if语句有两种基本形式:

    1. 基本形式
      if (expression)
      statement;

    2. if-else形式
      if (expression)
      statement1;
      else
      statement2;

    3. if-else if-else多分支形式
      if (expression1)
      statement1;
      else if (expression2)
      statement2;
      else
      statement3;

  • 工作原理

    • 条件表达式:计算括号内的表达式,如果结果为非零(true),则执行if后的语句;如果为0(false),则执行else后的语句(如果有)。
    • 语句执行:如果if后只有一条语句,可以不加花括号;如果有多条语句,必须使用花括号{}将它们括起来。
    • C语言特性:在C语言中,0表示false,非0表示true。例如:
if (3) { // 3是非0,条件为true
    printf("yes");
} else {
    printf("no");
}
// 输出:yes
  • 嵌套if语句
if (condition1) {
    if (condition2) {
        // 条件1和条件2都满足时执行
    } else {
        // 条件1满足但条件2不满足时执行
    }
} else {
    // 条件1不满足时执行
}
  • 最佳实践

    1. 使用大括号:即使只有一条语句,也建议使用大括号,提高代码可读性和可维护性。
    // 推荐
    if (condition) {
        statement;
    }
    
    // 不推荐(容易出错)
    if (condition)
        statement;
    
    1. 保持条件清晰:避免过于复杂的条件表达式,可以将复杂条件分解为多个简单条件。

    2. 注意条件顺序:在多条件判断中,条件应按优先级从高到低排列。

    3. 处理边界情况:确保所有可能的条件都被覆盖,避免遗漏导致逻辑错误。

    4. 避免过度嵌套:如果嵌套过深,考虑重构代码,使用函数或更简洁的逻辑。

悬空else问题

  • 悬空Else(问题是C/C++等编程语言中常见的语法问题,指的是当有多个嵌套的if语句时,else子句会与最近的if语句匹配,而不是与我们期望的if匹配。这通常是因为代码中省略了花括号({})导致的。

  • 核心规则: 在C语言中,else总是与最近的、没有else的if匹配,而不是与对齐的if匹配。

五.switch语句

  • 基本概念:switch语句是C语言中用于多分支条件判断的控制结构,它根据表达式的值选择执行不同的代码块。与if-else语句相比,当需要处理多个固定值的条件判断时,switch语句通常更为简洁、清晰。

  • 基本语法结构:

switch (expression) {//switch语句中的expression必须是整型(int、char等)、枚举类型或可以隐式转换为整型的表达式,不能是浮点数、字符串等类型。
    case constant1:
        // 执行内容
        break;
    case constant2:
        // 执行内容
        break;
    // 可以有多个case
    default:
        // 默认执行内容
        break;
}
  • 关键要素解析

    1. 表达式类型限制
      • 必须是整型(int/char):在C语言中,switch表达式必须是整型(包括字符型,因为字符本质上是小整数)
      • 不支持浮点型floatdouble等浮点类型不能用作switch表达式
      • 不支持字符串:不能直接用字符串进行比较

在C和C++语言中,switch语句里case后面的常量表达式必须是整型(包括字符型),不能是浮点型或者其他复杂类型。

  • case语句

    • 必须是常量表达式:case后必须接一个常量或宏定义,不能是变量
    int num = 1;
    switch(num) {
    case 1: // 正确,1是字面常量
        break;
    case num: // 错误,num是变量
        break;
       }
    
    • 常量必须唯一:case后的值不能重复
    case 5: // 正确
    case 5: // 错误,重复的值
    
    • 支持多个case匹配同一执行块
     case 10:
     case 9: // 10和9都执行同一个代码块
     printf("A");
     break;
    
  • break语句

    • 作用:终止switch语句的执行,防止"贯穿"到下一个case
    • 重要性:不使用break会导致程序继续执行后续case,这通常不是我们期望的行为
    • 特殊情况:有时有意要实现贯穿,可以不加break
case 1:
    printf("1");
case 2:
    printf("2"); // 没有break,会输出"12"
  • default子句

    • 可选:不是必须的,但建议保留以处理意外情况
    • 位置:可以放在任意位置,但通常放在最后
    • 作用:当所有case都不匹配时执行

如果case或default标签后有多条语句,使用{}以避免逻辑错误或警告。

  • 与if-else语句的对比
特点switch语句if-else语句
适用场景固定值条件判断任意条件判断
代码简洁性分支多时更简洁分支多时较冗长
条件类型仅支持整型/字符型支持任意类型
执行效率通常更高(直接跳转)逐个判断
代码可读性更清晰(固定值匹配)随分支增多变差
  • 典型应用场景
//星期几判断
int day = 3;
switch(day) {
    case 1:
        printf("Monday\n");
        break;
    case 2:
        printf("Tuesday\n");
        break;
    case 3:
        printf("Wednesday\n");
        break;
    // ... 其他星期
    default:
        printf("Invalid day\n");
}
  • 注意事项

    1. 始终使用break:除非有意要实现贯穿,否则每个case后都应添加break

    2. 保持case顺序:按逻辑顺序排列case,提高可读性

    3. 保留default:即使在所有已知情况下都能匹配,也建议保留default处理意外情况

    4. 使用花括号:当case后有多行语句时,使用花括号括起来

    5. 避免过度嵌套:如果逻辑复杂,考虑使用函数或重构代码

六. 循环辅助:break和continue语句

6.1 break语句

  • 基本作用
    • 立即终止当前循环:当执行到break语句时,会立即终止当前所在的 循环(for、while、do-while) ,并跳到循环后的下一条语句
 
 for() {
    if() {
        if() {
            if() {
                break; // 这个break会跳出for循环,而不是if语句
            }
        }
    }
}
  • 跳出switch语句:在switch结构中,break用于防止"贯穿"(fall-through),使程序跳出switch

  • 语法示例
    在循环中使用break

#include <stdio.h>
int main() {
    for (int i = 0; i < 10; i++) {
        if (i == 5) {
            break; // 当i等于5时,终止循环
        }
        printf("%d ", i);
    }
    // 输出: 0 1 2 3 4
    return 0;
}

在switch中使用break

#include <stdio.h>
int main() {
    int x = 1;
    switch (x) {
        case 1:
            printf("Red");
            break; // 跳出switch
        case 0:
            printf("White");
            break; // 跳出switch
    }
    // 不使用break时,会"贯穿"到下一个case
    return 0;
}
  • 重要特性
    • 多层循环中:只能跳出最内层的循环,无法同时跳出多层循环
    • 与if联用:通常与if条件语句一起使用,满足条件时跳出循环
    • 在switch中:必须使用,否则会"贯穿"执行后续case

6.2 continue语句

  • 基本作用

    • 跳过当前迭代:当执行到continue语句时,会跳过当前 循环 的剩余部分,直接进入下一次循环迭代
    • 不终止整个循环:只是跳过本次迭代,循环会继续执行
  • 语法示例

#include <stdio.h>
int main() {
    for (int i = 0; i < 10; i++) {
        if (i % 2 == 0) {
            continue; // 跳过偶数
        }
        printf("%d ", i);
    }
    // 输出: 1 3 5 7 9
    return 0;
}
  • 重要特性

    • 仅在循环中使用:只能在for、while、do-while等循环结构中使用
    • 与if联用:通常与if条件语句一起使用,满足条件时跳过当前迭代
    • 不会终止循环:只是跳过当前迭代的剩余代码
  • break与continue的区别

    特性breakcontinue
    作用终止当前循环,跳到循环后跳过当前迭代,进入下一次迭代
    是否终止循环
    执行效果循环体剩余代码不执行当前迭代剩余代码不执行
    在switch中可以使用不能使用
    在多层循环中只能跳出最内层循环只会影响当前循环

七. goto语句

  • 基本概念:goto语句是C语言中的一种无条件转移语句,允许程序直接跳转到函数内部的任意位置(只要在同一个函数内)。它通过标签(label)实现跳转,是C语言中一种较原始的控制流机制。
  • 基本语法
     // 标签定义
     label: // 标签标识符
    
     // goto语句
     goto label; // 跳转到label位置
    

C语言的goto语句中,标签(label)可以在任何地方,包括在goto语句之前、之后,甚至在goto语句的同一行。

  • 使用示例

    1. 基本示例
#include <stdio.h>
int main() {
    int myInteger = 5;
    goto myLabel; // 跳转到myLabel
    myInteger += 10; // 这行代码不会执行
myLabel: // 标签
    printf("myInteger=%d\n", myInteger); // 输出: 5
    return 0;
}
  1. 多层循环跳出
#include <stdio.h>
int main() {
    for (int i = 0; i < 10; i++) {
        for (int j = 0; j < 10; j++) {
            if (i == 5 && j == 5) {
                goto end; // 跳出两层循环
            }
        }
    }
end:
    printf("循环已跳出\n");
    return 0;
}
  1. 死循环示例
#include <stdio.h>
int main() {
    start: // 标签
        printf("This is a dead loop.\n");
        goto start; // 无条件跳转回start
}
  • 关键特性

    1. 跳转方向:可以向前跳转(跳过前面的代码),也可以向后跳转(跳入循环)

    2. 标签位置:标签必须与goto语句同处于一个函数中,但可以不在同一循环层中

    3. 标签规则

      • 标签名需符合标识符命名规则
      • 标签必须以冒号结尾
      • 不能重名
  • 使用场景与注意事项

    1. 合理使用场景(推荐使用)

      • 跳出多层嵌套循环:这是goto语句最合理的使用场景

      • 错误处理:在需要统一清理资源的场景中,可以使用goto跳转到错误处理代码

    2. 不推荐使用场景

      • 一般流程控制:用if-else或switch语句代替
      • 循环控制:用break和continue代替
      • 创建死循环:应使用while(true)等结构化方式
    3. 重要注意事项

      • 避免滥用:过度使用goto会使代码难以理解和维护,形成"面条代码"
      • C语言作者建议:知识库[4]指出:“其实就连C语言的作者也觉得goto语句非常容易被滥用,并且建议一定要谨慎使用,甚至根本不用它。”
      • 不能从复杂状态跳转:知识库[8]指出:“不能从一段复杂的执行状态中的位置goto到另外一个位置,比如,从多重嵌套的循环判断中跳出去就是不允许的。”
      • 避免双向跳转:知识库[8]强调:“应该避免向两个方向跳转。这样最容易导致’面条代码’。”
  • goto语句 vs 其他控制流

    控制流适用场景优点缺点
    goto跳出多层循环、统一错误处理代码简洁难以理解、易出错
    break跳出单层循环清晰、标准无法跳出多层循环
    continue跳过当前循环迭代清晰、标准无法跳出循环
    if-else/switch条件分支清晰、结构化分支多时代码冗长
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值