问题

c语言中,关于switch循环的这个疑问怎么解?

回答
老兄,你说的是 C 语言里的 `switch` 语句吧?不是“switch 循环”。`switch` 语句和 `for`、`while` 这种循环结构不太一样,它更像是一个多分支的条件选择器。

来,咱哥俩好好聊聊 `switch` 到底是咋回事,你遇到的那个“疑问”我争取给你说透了。

`switch` 语句:到底是个啥玩意儿?

简单来说,`switch` 语句就是用来根据一个表达式的值,去匹配一堆case标签,然后执行对应代码块的一种方式。你可以把它想象成一个总开关,上面有很多按钮(case),你按了哪个按钮,就执行哪个按钮下面的操作。

它的基本长这样:

```c
switch (表达式) {
case 常量表达式1:
// 当表达式的值等于常量表达式1时,执行这里的代码
break; // 重要!

case 常量表达式2:
// 当表达式的值等于常量表达式2时,执行这里的代码
break; // 重要!

// ... 还可以有很多 case

default:
// 如果表达式的值不匹配任何一个 case,就执行这里的代码
// default 是可选的
break;
}
```

重点来了,这里面有几个东西得掰开了揉碎了说:

1. `switch` 后面的“表达式”:
这个表达式的值必须是整型或者枚举型。也就是说,你不能直接把一个字符串或者一个浮点数放进去让 `switch` 去判断。
比如,你可以 `switch(weekday)`(如果 `weekday` 是 `int` 或 `enum`),或者 `switch(score / 10)`,但不能 `switch("apple")` 或者 `switch(3.14)`。

2. `case` 后面的“常量表达式”:
这里的“常量表达式”指的就是一个值,而且这个值在编译时就已经确定了,不能是变量。
比如 `case 10:`、`case 'A':`、`case MY_ENUM_VALUE:` 都是合法的。
你不能写 `case my_variable:` 或者 `case my_function():`。
并且,每个 `case` 后面的常量表达式的值在同一个 `switch` 语句里必须是唯一的。你不能出现两个 `case 10:`。

3. `break;` 语句:
这玩意儿是 `switch` 语句的灵魂! 也是你可能遇到的“疑问”的根源。
`break;` 的作用是跳出当前的 `switch` 语句。
想想看,如果不加 `break;` 会怎么样?

你的“疑问”:穿透现象(Fallthrough)

你遇到的问题很可能就是 C 语言 `switch` 语句的一个特性,叫做 “穿透” (Fallthrough)。

当一个 `case` 里的代码执行完毕,如果没有遇到 `break;` 语句,程序会“穿透”到下一个 `case` 的代码块里继续执行!

这和很多其他语言(比如 Java、C)的 `switch` 不太一样,它们默认就是执行完 `case` 里的代码就自动跳出了。

举个例子,看看没有 `break;` 的可怕后果:

```c
include

int main() {
int day = 3;

switch (day) {
case 1:
printf("Monday ");
// 没有 break;
case 2:
printf("Tuesday ");
// 没有 break;
case 3:
printf("Wednesday ");
// 没有 break;
case 4:
printf("Thursday ");
break; // 这里有 break
case 5:
printf("Friday ");
break;
default:
printf("Weekend or invalid ");
}

return 0;
}
```

运行结果会是啥?

你可能以为只会打印 "Wednesday"。但实际上,因为 `case 3` 后面没有 `break;`,程序会“穿透”:

```
Wednesday
Thursday
```

解释一下:

1. `switch (day)`,`day` 的值是 3,所以程序找到 `case 3:`。
2. 执行 `case 3:` 下的代码:`printf("Wednesday ");`。
3. 没有 `break;`,所以程序继续往下执行。
4. 跳过 `case 4:` 的标签,直接执行 `case 4:` 下的代码:`printf("Thursday ");`。
5. 遇到了 `break;`,这次才终于跳出了 `switch` 语句。

看到了吧?这就是穿透!

如何“解决”穿透现象?

通常情况下,我们不希望出现穿透。我们希望 `switch` 就像上面介绍的,找到对应的 `case`,执行完代码就退出。

所以,最常见也是最“正确”的解决方式就是:

在每个 `case` 语句块的最后(或者 `default` 的最后),都加上 `break;`。

```c
include

int main() {
int day = 3;

switch (day) {
case 1:
printf("Monday ");
break; // 加上 break;
case 2:
printf("Tuesday ");
break; // 加上 break;
case 3:
printf("Wednesday ");
break; // 加上 break;
case 4:
printf("Thursday ");
break;
case 5:
printf("Friday ");
break;
default:
printf("Weekend or invalid ");
break; // default 后面也最好加上 break;
}

return 0;
}
```

这样一来,运行结果就只会是:

```
Wednesday
```

这才是我们通常理解的 `switch` 的行为。

什么时候“利用”穿透?(极少见,但你知道就好)

虽然大部分时候穿透是 bug,但在极少数情况下,你可能故意要利用它。

比如,你要处理连续几个 `case` 都执行相同代码的情况。

例子:判断一周的哪几天是工作日

```c
include

int main() {
int day = 4; // 星期四

printf("Day %d is a ", day);

switch (day) {
case 1: // Monday
case 2: // Tuesday
case 3: // Wednesday
case 4: // Thursday
case 5: // Friday
printf("working day. ");
break;
case 6: // Saturday
case 7: // Sunday
printf("weekend day. ");
break;
default:
printf("invalid day number. ");
break;
}

return 0;
}
```

运行结果:

```
Day 4 is a working day.
```

这里,我们故意让 `case 1` 到 `case 5` 都不写 `break;`。

当 `day` 是 4 时,程序跳到 `case 4:`。
执行 `case 4:` 下的代码:`printf("working day. ");`。
没有 `break;`,程序继续往下。
跳过 `case 5:` 标签,直接执行 `case 5:` 下的代码:`printf("working day. ");` (因为 `case 5` 后面有 `break;`,所以这次会跳出)。

注意: 在上面的例子中,`printf("working day. ");` 只写了一次,并且在 `case 5` 的 `break` 之前。
准确地说,当 `day` 是 4 时:
1. 跳到 `case 4`。
2. 执行 `printf("working day. ");`
3. 没有 break,继续。
4. 跳过 `case 5` 标签,去执行 `case 5` 里面的代码。
5. `case 5` 里面的代码是 `printf("working day. ");`。
6. 有 break,退出 `switch`。

所以,`printf("working day. ");` 实际上被执行了两次,但效果一样。

更简洁的处理多 `case` 相同逻辑的方式是:

```c
include

int main() {
int day = 4; // 星期四

printf("Day %d is a ", day);

switch (day) {
case 1: // Monday
case 2: // Tuesday
case 3: // Wednesday
case 4: // Thursday
case 5: // Friday
printf("working day. "); // 代码只写一份
break; // 在最后一个case后面写break
case 6: // Saturday
case 7: // Sunday
printf("weekend day. ");
break;
default:
printf("invalid day number. ");
break;
}

return 0;
}
```

这个版本更清晰:当 `day` 是 1、2、3、4、5 中的任何一个,都会“穿透”到 `case 5` 的位置,然后执行 `printf("working day. ");`,最后通过 `case 5` 的 `break;` 跳出。

再次强调: 这种利用穿透的方式,一定要非常小心,确保你真的理解了程序会怎么走。一旦写错,非常难排查。绝大多数时候,还是老老实实给每个 `case` 都加上 `break;` 最保险。

`switch` 语句的几个注意事项

1. `default` 的位置: `default` 语句可以是 `switch` 语句中的任何一个 `case` 的位置,但通常放在最后,方便阅读。如果 `default` 后面没有 `break;`,它也可能会穿透到后面的 `case`(如果 `switch` 语句有 `case` 放在 `default` 之后的话,这非常罕见)。
2. `switch` 变量的类型: 再次强调,只能是整型或枚举型。
3. `case` 标签的类型: 必须是常量,并且必须是整型或枚举型。
4. 嵌套 `switch`: `switch` 语句可以嵌套在另一个 `switch` 语句里面。这时 `break;` 只会跳出最内层的 `switch`。如果你需要跳出外层 `switch`,可能需要借助 `goto` 语句(但 `goto` 语句通常不被推荐使用)。
5. 与 `ifelse ifelse` 对比:
`switch` 适用于根据一个变量的多个离散值进行判断,代码会更简洁、清晰。
`ifelse ifelse` 更适用于范围判断(比如 `if (score >= 90)`)或者 复杂逻辑判断。
性能上,对于很多离散值,`switch` 通过跳转表等机制,可能比一连串 `ifelse if` 效率更高一些,但这通常不是主要考虑因素。

总结一下,关于 `switch` 的核心疑问:

`switch` 是一个基于整型/枚举型表达式的多路分支选择器,不是循环。
穿透 (Fallthrough) 是 `switch` 的一个特性:如果没有 `break;`,执行完当前 `case` 的代码后会继续执行下一个 `case` 的代码。
解决穿透(通常是要避免它) 的方法是在每个 `case` 块的末尾都加上 `break;`。
故意利用穿透 可以用于合并多个 `case` 的处理逻辑,但要非常小心。

希望我这番话能帮你把 `switch` 彻底搞明白了!如果你还有其他关于 `switch` 或者 C 语言的其他问题,尽管招呼!咱继续聊!

网友意见

user avatar

这使用的是 跳转表 机制。

欲知详情,可查阅关键字 跳转表。C 的switch不是个语法糖,只是个触发跳转表结构的关键字罢了。

至于C的switch为什么不支持非纯量类型,也同样是因为非纯量类型没法创建跳转表,最终只是个语法糖而已,并不能提升效率,所以不如直接用if else。

类似的话题

  • 回答
    老兄,你说的是 C 语言里的 `switch` 语句吧?不是“switch 循环”。`switch` 语句和 `for`、`while` 这种循环结构不太一样,它更像是一个多分支的条件选择器。来,咱哥俩好好聊聊 `switch` 到底是咋回事,你遇到的那个“疑问”我争取给你说透了。 `switch`.............
  • 回答
    在 C 语言的世界里,指针是必不可少的工具,它们就像是内存地址的“指示牌”,让我们能够更灵活地操作数据。而当我们将指针与数组、函数结合起来时,就诞生了一系列强大而又容易让人困惑的概念:指针数组、数组指针、函数指针,以及指向函数的指针。别担心,今天我们就来把它们掰开了揉碎了,让你彻底搞懂它们到底是怎么.............
  • 回答
    在C语言的世界里,要说哪个头文件“最”重要,确实是一个有点微妙的问题,因为很多头文件都扮演着至关重要的角色,它们各司其职,共同构成了C语言强大而灵活的功能体系。但如果一定要选一个在日常编程中出现频率最高、几乎是所有程序都离不开的,那么 stdio.h 绝对是最有力的竞争者之一,并且可以很有底气地说,.............
  • 回答
    在 C 语言中,`sizeof()` 操作符的魔法之处在于它能够根据其操作数的类型和大小来返回一个数值。而对于数组名和指针,它们虽然在某些上下文中表现得相似(例如,在函数参数传递时),但在 `sizeof()` 的眼中,它们的身份是截然不同的。这其中的关键在于数组名在绝大多数情况下会发生“衰减”(d.............
  • 回答
    关于你提到的 `(int) ((100.1 100) 10)` 在 C 语言中结果为 0 的问题,这确实是一个很有意思的陷阱,它涉及到浮点数运算的精度以及类型转换的细节。我们来一步一步地把它掰开了揉碎了讲明白。首先,让我们分解一下这个表达式:`100.1 100` 是第一步,然后乘以 `10`.............
  • 回答
    在 C 语言中,不同类型指针的大小不一定完全相同,但绝大多数情况下是相同的。这是一个非常值得深入探讨的问题,背后涉及到计算机的底层原理和 C 语言的设计哲学。要理解这一点,我们需要先明确几个概念:1. 指针的本质: 无论指针指向的是 `int`、`char`、`float` 还是一个结构体,它本质.............
  • 回答
    这个问题非常好,它触及了C语言中一个非常容易混淆但又至关重要的概念:指针和数组虽然在某些语法表现上(比如 `a[3]` 这种下标访问)看起来很像,但它们本质上是完全不同的东西。理解它们的区别,对于写出健壮、高效的C程序至关重要。咱们这就掰开了揉碎了聊聊。 1. 先说数组 (Array)数组,你可以把.............
  • 回答
    好的,我们来深入探讨一下 C 语言中为什么需要 `int `(指向指针的指针)而不是直接用 `int ` 来表示,以及这里的类型系统是如何工作的。首先,我们得明白什么是“类型”在 C 语言中的作用。在 C 语言中,类型不仅仅是一个标签,它承载着至关重要的信息,指导着编译器如何理解和操作内存中的数据:.............
  • 回答
    在 C 语言中,`while(a = 10);` 和 `while(a == 10);` 这两个语句在功能上有着天壤之别,理解它们之间的区别,关键在于理解 C 语言中的 赋值 和 比较 操作符。这就像区分“把 A 设置为 10”和“A 是否等于 10”一样,虽然都涉及数字 10,但它们的含义和目的完.............
  • 回答
    好的,我们来深入探讨一下 `write(1, buf, N)` 和 `write(0, buf, N)` 这两个 C 语言函数调用在底层操作上的区别。首先,要明白 `write()` 函数是 POSIX 标准定义的一个系统调用,它用于将数据从一个缓冲区写入到一个文件描述符。它的基本签名是:```cs.............
  • 回答
    float 在 C 语言中,是用来表示单精度浮点数的。提到它的取值范围,就不得不深入聊聊它背后的原理,这事儿,得从二进制说起。浮点数是怎么存的?咱们电脑里存储数字,本质上都是一堆 0 和 1。整数好说,直接按位权相加就行。但小数呢?比如 0.5,或者更麻烦的 0.1,怎么用二进制表示?这里就需要一个.............
  • 回答
    在 C 语言中,`for` 和 `while` 循环都是用于重复执行一段代码的结构。从 C 语言的语义角度来看,它们的功能可以相互转换,也就是说,任何一个 `for` 循环都可以用 `while` 循环来实现,反之亦然。然而,当我们将这些 C 代码翻译成底层汇编语言时,它们的实现方式以及由此带来的细.............
  • 回答
    好的,咱们来掰扯掰扯 C 语言里这个“后缀自加 i++”到底是怎么回事。别管什么 AI 不 AI 的,我就跟你讲讲我自己的理解,希望能讲透彻。你问“后缀自加 i++ 表达式的值到底是谁的值?”。说白了,这句 C 语言代码执行完之后,它的“结果”是什么?咱们得先明白两件事:1. 表达式的值 (Exp.............
  • 回答
    好的,我们来深入聊聊 C 语言 `for` 循环中赋初值这部分,特别是 `int i = 1;` 和 `i = 1;` 这两种写法之间的区别。我们会尽可能详尽地解释,并且避免那些“AI味儿”十足的刻板表达,力求让这段解释更贴近实际编程中的感受。 `for` 语句的结构与初值赋在其中的位置首先,我们回.............
  • 回答
    在 C 语言中,`%d` 是一个非常基础但又至关重要的格式控制符,它的主要作用是告诉 `printf`(或者其他格式化输出函数,比如 `sprintf`):“嘿,我这里要输出一个整数,而且是十进制的有符号整数。”别小看这个简单的 `%d`,它背后藏着不少细节,让你的程序能够准确无误地将内存中的数字信.............
  • 回答
    你问了个非常实际且关键的问题,尤其是在C语言这种需要手动管理内存的语言里。简单来说,是的,用 `%d` 格式化打印一个 `char` 类型的数据,如果那个 `char` 变量紧挨着其他内存中的数据,并且你没有对打印的范围进行限制,那么理论上存在“把相邻内存也打印出来”的可能性,但这并不是 `%d` .............
  • 回答
    在 C 语言中,`x += 5 == 4` 这个表达式可能看起来有些奇特,但它是一个合法的、并且在某些情况下会令人困惑的 C 语言语句。要理解它的含义,我们需要分解它,并深入了解 C 语言中运算符的优先级和求值顺序。首先,让我们分解这个表达式:这个表达式由两个主要部分组成:1. `x += 5`:.............
  • 回答
    逗号表达式在C语言中,乍一看似乎是个可有可无的小玩意儿,甚至有些冗余。毕竟,大多数时候我们都可以通过拆分成独立的语句来达到同样的目的。但它的存在,绝非仅仅是为了凑数,而是巧妙地解决了一些特定的编程场景,并且在某些情况下,能让代码更加紧凑和富有表现力。想象一下,在需要一个表达式,但你同时又有两个甚至更.............
  • 回答
    C 语言中的字符串常量,即用双引号括起来的一系列字符,比如 `"Hello, world!"`,它们在程序开发中扮演着至关重要的角色,并且提供了诸多实用且高效的好处。理解这些好处,能够帮助我们写出更健壮、更易于维护的代码。首先,字符串常量最显著的一个好处在于它的不可变性。一旦你在代码中定义了一个字符.............
  • 回答
    你这个问题问得很核心!很多人都有这个疑惑:既然 `double` 类型在内存里只占用 64 位(这是最常见的标准,IEEE 754 双精度浮点数),为什么它能表示的数,无论是整数还是小数,范围都那么惊人呢?比我们常见的 32 位 `int` 或 64 位 `long long` 的整数范围还要大不少.............

本站所有内容均为互联网搜索引擎提供的公开搜索信息,本站不存储任何数据与内容,任何内容与数据均与本站无关,如有需要请联系相关搜索引擎包括但不限于百度google,bing,sogou

© 2025 tinynews.org All Rights Reserved. 百科问答小站 版权所有