目录
- 一.switch与case关键字
- 1.基本语法结构
- 2.case的作用
- 3.break与continue的作用
- 4.case注意事项
- 5.default关键字
- 6.底层原理
- 7.与if else的核心区别
- 8.跳转表的拓展内容
- 二.do-while while for循环
- 1.三种循环各自语法
- 2.break与continue区别
- (1)break:直接终止整个循环(所有循环通用)
- (2)continue:跳过本次循环剩余部分,进入下一次循环
- 3.循环语句的注意事项
- 三.goto语句
- 1.核心语法与底层逻辑
- 2.适用场景
- 3.禁忌与风险
- 4.核心总结
- 四.void关键字
一.switch与case关键字
switch case语句是C语言中多分支整型常量判断语句,核心优势是通过跳转表实现快速跳转,比多层if语句更加简洁高效,专门为单个变量匹配多个固定的整形常量场景设计。
1.基本语法结构
以下是switch选择结构的基本语法结构
switch (整形表达式)
//表达式必须是char/short/int/long(含无符号类型)、枚举类型
{
case var1:
//var为整形常量,常量必须是编译期确定的整形值(字面量、宏定义、枚举常量)
break;//可选,终止分支,跳出整个switch语句
case var2:
break;
default://可选,所有case语句不匹配时执行。
break;//放最后可以不需要break,执行完自动退出。
}
语法规则:
- 表达式支持的类型,仅仅支持整形相关的类型。例如:char short int long longlong(含无符号版本)。枚举类型(本质上是整形类型的映射)。禁用类型:float和double精度无法精准匹配,字符串(C语言无String类型,char[]不能直接作为表达式),指针(语法允许,但是实际运用毫无作用)。
- case的三个要求:(1)必须是整形常量表达式,字面量、宏定义、枚举常量不能是变量。(2)常量值不可重复,多个case不能有相同的值,编译会直接报错。(3)多个case有穿透特性,可以利用这个特性写出特别的代码。
- default的三个细节:(1)可选择加不加。但是推荐加。处理无效输入,提升代码的健壮性。(2)位置灵活,可以放在switch的任何位置,但是一般放在switch语句的最后位置。(不在最后位置时,需要加上break关键字防止case穿透)。
进阶用法和实践代码:
1.case的合理穿透
while (1)
{
scanf("%d",&day);
switch (day)
{
case 1:
case 2:
case 3:
case 4:
case 5:
printf("周内");
break;
case 6:
case 7:
printf("周末");
break;
default:
printf("输入错误");
}
}
上述代码的具体解释:根据输入值的不同,进行匹配对应的星期,进而进行打印。因为case的穿透特性(前5个case没有加break,导致只要输入1-5时,就会输出1-5条件对应的周内。反之输出周末)
2.枚举+switch语句
enum Weekday
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
};
// 枚举默认自增
int main()
{
enum Weekday day = WED;
switch (day)
{
case MON:
case TUE:
case WED:
case THU:
case FRI:
printf("周内\n");
break;
case SAT:
case SUN:
printf("周末\n");
break;
}
return 0;
}
上述代码的具体解释:合理运用枚举类型,提高代码的可读性。
2.case的作用
在上述语法结构的学习中,我们了解了关键字switch与case的相关语法,下面我们来学习case的作用。
case的作用是匹配switch表达式的值,当值匹配时,执行case语句后的值,直到遇到break终止执行。
3.break与continue的作用
break在switch语句中,可以跳过整个代码块。在循环中,能跳过整个循环。
continue在循环语句中,可以跳过本次循环,直接进入下一次循环迭代,不终止整个循环。
4.case注意事项
- 必须搭配switch使用:case是switch语句的分支标签,不能单独存在,且switch后需跟表达式。
- 记得加上break或者return:case执行完后需要用break终止分支,否则会穿透到下一个case语句。(除非刻意为之)
- case后跟的是常量表达式:值必须是字面量,不能是变量、只读变量或动态计算值。
- default可选但建议加:当所有的case都不匹配时执行,通常放在最后(位置没有限制),用于处理异常情况。
5.default关键字
- 可以放在switch语句的任意位置(不一定放在最后),但是放最后时不需要加上break。
- 可选择是否加上default关键字,但是推荐加。用于处理异常或者未预料的输入,让代码更加健壮。
- 不依赖case数量,哪怕有多个case,只要无匹配项,就会执行default后的语句。
6.底层原理
编译器会将所有的case常量按照升序整理成跳转表(本质是数组,索引等于常量值,元素等于代码地址)。
先计算switch表达式的值,自动转换为int类型,通过跳转表直接定位到对应的case语句,不需要逐个进行判断。
如果case后面没有break return或goto,会出现穿透现象,执行下一个case继续执行。
7.与if else的核心区别
| 语法结构 | switch-case语句 | if-else语句 |
|---|---|---|
| 适用场景 | 单个变量匹配(整形常量) | 范围判断(动态条件) |
| 执行效率 | 高(跳转表直接定位) | 低(分支越多越慢) |
| 可读性 | 分支多有优势(简洁) | 分之少时有优势(灵活) |
核心结论:整形常量多分之用switch,范围条件判断用if语句。
8.跳转表的拓展内容
跳转表是存储函数入口地址或代码标签地址的连续数组。核心是通过索引直接映射目标地址,实现O(1)效率的跳转,替代多分之判断,底层靠内存地址计算+间接跳转指令提速。
简单来说就是用数组做地址目录,索引对应目标编号,不用逐个比较条件,直接按照编号找到要执行的代码地址,分支越多效率越明显。
跳转表的底层本质:是一块连续的内存空间(数组),存储的事函数入口地址或代码段标签的地址。核心是靠索引地址的直接映射,绕开多分之判断的比较逻辑,本质是用空间换取时间,底层执行完全依赖CPU的地址访问和跳转指令。
| 对比准度 | switch语句 | 跳转表 |
|---|---|---|
| 本质 | 高级语言语法结构,用于多分支判断 | 底层数据结构(数组),存储地址实现直接跳转 |
| 底层实现 | 分支连续时编译器优化为跳转表O(1),稀疏时退化为if-else链 | 强制O(1)效率,手动控制地址映射,不受编译器影响 |
| 适用场景 | 简单值匹配。分支逻辑简短 | 分支数量多,逻辑复杂,或需手动控制跳转逻辑。 |
| 灵活性 | 只能匹配特定制逻辑嵌套受限 | 可动态修改数组元素,支持跨模块函数映射 |
| 底层开销 | 编译器自动处理地址计算。无需手动管理 | 需手动维护数组,检查索引合法性,底层可控性强 |
简单来说switch是省心的语法糖,编译器会帮你确定是否用跳转表。跳转表是手动优化工具,直接掌控底层跳转逻辑,适合追求极致效率或者复杂分支的场景。
二.do-while while for循环
上述是对C语言选择结构switch的讲解,下面来讲解一下循环结构。
1.三种循环各自语法
//while循环
条件初始化
while (条件判定)
{
//业务更新
//条件更新
}
//for循环
for (条件初始化;条件判定;条件更新)
{
//业务代码
}
//do while循环
条件初始化
do
{
//业务代码
//条件更新
}
while(条件判定)
2.break与continue区别
(1)break:直接终止整个循环(所有循环通用)
作用:立即跳过当前所在的循环体(整个循环),执行循环后面的代码,循环不再继续。
for循环:触发break后,直接跳过更新表达式和后续判断,终止整个循环。
while循环:触发break后,直接终止循环,不再执行条件判断和循环体剩余部分。
do-while循环:触发break后,直接终止循环,跳过后续的条件判断。
(2)continue:跳过本次循环剩余部分,进入下一次循环
作用是:跳过循环体中continue之后的代码,直接进入下一次循环的条件判断(for循环特殊)。
for循环:触发continue后,跳过循环体剩余代码,执行更新表达式,再判断条件。
while循环:触发continue后,跳过循环体剩余代码,直接判断条件,需要手动在continue前更新变量,否则会陷入死循环。
do-while循环:触发continue后,跳过循环体剩余代码,执行条件判断(满足则礼物循环,同样需要手动提前更新变量)
3.循环语句的注意事项
- 避免死循环:for循环确保条件会终止,while或者do while循环需要再在循环内修改判断变量。
- 循环变量初始化:for循环变量初始化不应该遗忘,while循环前应确保判断变量有有效值。
- 循环体执行逻辑:单个语句也建议加上大括号,避免后续扩展代码出错。
- break或continue谨慎使用,明确跳出循环的范围,嵌套循环避免使用该关键字,有可能会导致逻辑混乱。
- 注意边界值,处理数组、集合或循环条件时,别超出索引范围。
三.goto语句
C语言goto语句是无条件跳转的直接通道,核心作用是跳过中间代码,直接跳转到同一函数内的指定标签处,底层依赖CPU的跳转指令,无任何条件判断,灵活但风险较高。
1.核心语法与底层逻辑
用法:goto标签名(执行后立刻跳转到对应标签,执行标签后的代码)
底层本质:标签本质是代码段中某条指令的内存地址,goto语句编译后会产生标签地址的指令,直接跳转到目标地址,跳过中间所有代码的执行。
2.适用场景
- 跳出多层循环:无需逐层用break,直接跳出所有的嵌套循环。比多层break更加简洁。
- 统一错误处理:函数内多个分支出错时,跳转到同一清理标签(释放内存,关闭文件),避免重复代码。
- 简化复杂条件跳转:替代多层嵌套if-else让代码流程更加清晰。
3.禁忌与风险
- 跨函数跳转:严禁跳转到其他函数的标签(底层是访问非法代码段,触发段错误)
- 跳过变量初始化:不能跳转到未初始化变量的使用处(底层导致变量值不确定,程序行为异常)
- 滥用导致代码混乱:频繁跳转形成可读性和维护性差的代码
- 跳过资源分配和释放:错误使用goto语句,导致没有进行资源分配和空间释放,进而出现代码错误。
4.核心总结
goto语句的核心是无条件、跨段跳转。优势是跳转直接高效、劣势是破坏代码结构化流程、但是能不用则不用,用就应该有万全的把握。
四.void关键字
void本质上是无类型,核心作用是限制数据或函数的类型关联性,底层靠编译器语法检查和内存分配规则实现,具体用法集中在三个场景:
- 函数返回值:表示无返回值。
核心作用:明确函数执行后不返回任何数据,调用者不能接收返回值。
底层逻辑:编译器不会为函数生成返回值传递指令,函数执行完直接返回,无需额外的内存开销。 - 函数参数:表示无参数。
核心作用:明确函数不接收任何参数,避免调用时,传入多余参数导致语法错误。 - 指针类型:万能指针。
核心作用:可以指任意类型的数据,是唯一能直接接收其他类型指针的指针类型。
底层逻辑:void仅存储内存地址但没类型信息,编译器无法确定引用后的内存长度。