文心快码Rules:让AI写出的代码不再偏离项目规范
介绍文心快码的Rules功能,怎样用结构化规则文件把项目约定注入AI,让代码生成自动对齐架构和团队规范。
支持通配符SSL证书、多域名证书、IP证书。适配ACME接口, 支持Zerossl、Let's Encrypt和Google等渠道。登录已有账号
2026-08-04 19:15:37 C语言 switch-case fall-through 代码规范 最佳实践
switch-case这个结构,写C的人基本天天用。但用归用,有些细节可能一直没太在意,直到某天线上出了问题、或者code review被人指出来,才回头去翻标准。
这篇文章把我这些年关于switch-case的一些理解和踩过的坑整理一下。
最完整的写法是这样:
switch (表达式) {
case 常量表达式1:
语句1;
break;
case 常量表达式2:
语句2;
break;
default:
语句n;
break;
}
执行流程其实就三步:先算switch后面那个表达式的值,然后从上到下找匹配的case,找到了就执行,直到碰见break或者整个switch结束才跳出来。
如果所有case都没匹配上,就走default——前提是你写了default。
这里有几个规则,很多人第一次学的时候没当回事,后面写代码就出问题了。
第一个,switch后面的表达式只能是整型。
int、char、enum都可以,但float不行,字符串更不行。你写个float x = 1.1然后switch(x),编译器直接报错。我见过有人用浮点数做状态码然后想switch,编译不过才反应过来。
float x = 1.1;
switch (x) { // 编译报错,浮点类型不允许
case 1.1: printf("case 1.1"); break;
}
第二个,case后面的值必须是编译期就能确定的常量。字面量可以,宏可以,枚举值可以,但变量不行。而且同一个switch里case值不能重复。
switch (x) {
case 1+1: // 这个可以,编译器能算出是2
case 2: // 这个不行,和上面重复了
printf("x equals 2"); break;
}
第三个,break不是语法强制要求的。你不写break,程序就顺着往下执行,直接掉进下一个case里。这就是所谓的"穿透"或者叫fall-through。
看下面这个例子:
int x = 2;
switch (x) {
case 1: printf("x=1\n");
case 2: printf("x=2\n");
case 3: printf("x=3\n");
default: printf("x>3\n");
}
输出是什么?是四行:x=2、x=3、x>3。因为case 2匹配成功后,没有break拦着,一路穿透下去把后面所有case的代码都执行了。
这玩意十有八九是忘写break导致的bug。尤其是项目里switch分支很多的时候,漏掉一个break,测试还不一定能覆盖到,上线之后某个特定条件触发,行为就不对了。
不过穿透也不全是坏事。多个case需要执行同一段逻辑的时候,故意利用穿透可以让代码更简洁。比如判断月份有多少天:
switch (month) {
case 4: case 6: case 9: case 11:
printf("30天"); break;
case 2:
printf("28或29天"); break;
default:
printf("31天");
}
4、6、9、11这四个月都是30天,把它们的case叠在一起,共用一个处理逻辑,比每个case都写一遍break要干净。
这种写法在嵌入式开发里处理状态机的时候尤其常见。
但注意,如果你是刻意使用穿透,最好在代码里加个注释说明一下,比如/* fall through */,不然review的人会纠结你这是故意的还是忘写了。
有些团队甚至会在lint规则里强制要求穿透处必须加注释。
最常规的用法就是多选一的菜单处理:
int day;
printf("请输入1-7的数字:");
scanf("%d", &day);
switch (day) {
case 1: printf("星期一"); break;
case 2: printf("星期二"); break;
default: printf("输入错误");
}
还有一种情况是case里需要定义局部变量。这时候得用花括号把case后面的代码包起来,不然作用域会出问题:
switch (choice) {
case 1: {
float radius, area;
printf("输入半径:");
scanf("%f", &radius);
area = 3.14159 * radius * radius;
printf("圆面积:%.2f", area);
break;
}
case 2: {
float side, area;
// 正方形面积计算...
}
}
不加花括号的话,case 1里定义的变量在case 2里也能访问到——虽然case 2可能根本没走到case 1那一步,但编译器不让你这么干,会报变量定义跳过的错误。这个问题新手期基本都遇到过。
这两个东西都能做分支,但适用场景差挺多的。
switch-case要求表达式是整型,条件判断只能是值匹配,不能写大于小于这种范围判断。好处是分支多了以后性能好——编译器可以把case值做成跳转表,O(1)就能定位到对应分支。
if-else是逐个判断,分支一多就是O(n)走下来。
所以经验上的选择标准是:分支条件是具体的常量值、而且数量超过四五个的时候,优先switch-case。需要做范围判断、或者条件里有复杂逻辑表达式的时候,用if-else。
也别太纠结性能差异,现代编译器优化做得很好,很多情况下生成的汇编差不多。更重要的其实是可读性——一堆常量值匹配写成if-else链真的很难看。
最后整理一下平时写switch-case容易翻车的地方:
忘写break排第一。这个没什么好说的,养成习惯,写完每个case先敲break再写逻辑。
case值重复排在第二。看起来很低级但真的会犯,尤其是用宏定义或者枚举的时候,改了一个忘改另一个,编译报错了才反应过来。
表达式类型不对排第三。浮点数想switch,或者拿指针想switch,编译不过。C语言的标准就这么定的,只能整型。
default分支没写break。
default后面理论上不需要break因为已经到switch末尾了,但如果你后来在default后面又加了个case,那就出事了。所以建议default也写break,保持一致。
几个实践上的建议:
每个case后默认加break,只有刻意使用穿透时才省略,而且加上注释说明。
default分支不要省略。
哪怕你确定所有可能的case值都覆盖了,也写个default兜底,里面打个日志或者做个异常处理。线上出问题的时候你会发现这个习惯能省很多排查时间。
case的顺序按常量值从小到大排列,这样看代码的人能快速定位。
如果是枚举类型,switch要确保覆盖所有枚举值,编译器有时候会给警告,别忽略。
如果case里面的逻辑比较复杂,用花括号包起来,避免变量作用域的麻烦,也方便以后加代码。
介绍文心快码的Rules功能,怎样用结构化规则文件把项目约定注入AI,让代码生成自动对齐架构和团队规范。
搞懂cJSON里内存释放的正确姿势,避开野指针和内存泄漏的常见坑。
聊一聊 strncmp 的比较逻辑、容易踩的坑,以及几种空字符串初始化方式的区别和选用场景
cJSON库在处理JSON数据时涉及大量动态内存分配。本文将深入剖析cJSON_Delete函数的工作原理,指导开发者如何检查cJSON_Parse返回值、避免重复释放,以及通过规范操作彻底杜绝内存泄漏,确保程序高性能与稳定性。
还在为C语言字符串比较和初始化头疼?本文揭秘`strncmp`函数的实战技巧,教你如何精准比较字符串前N个字符。同时,提供多种字符串空值初始化的实用方案,助你避免常见陷阱,写出更安全、更高效的C代码。即学即用,告别Bug!