故障过程
1, 上午的时候, QA 同学突然说, 测试自动化的流程突然过不去了, 问我是不是最近对线上做了某些修改. 当时第一反应是不可能
2, 通过 QA 同学提供的 test case, 在测试环境通过 curl 发送请求, 发现果然广告返回值跟预期不符.
3, 通过 Git log 对比, 发现近期只有一个 switch 语句有修改.
4, 尝试在代码中加入 log 语句, 发现日志输出果然跟 QA 的错误结果一致, 至此原因找到.
故障原因
下面是错误代码
- switch (dsp_res->bid_type()) {
- case 0:
- {
- auto info = dsp_response->add_dsp_res_infos();
- info->set_dsp_id(item.dsp_id());
- if (dsp_res->has_cache_duration()) {
- info->set_duration(dsp_res->cache_duration());
- }
- if (dsp_res->has_quality()) {
- info->set_ratio(dsp_res->quality());
- }
- // do sth
- }
- case 1:
- break;
- case 2:
- {
- auto info = dsp_response->add_dsp_res_infos();
- if (dsp_res->has_cache_duration()) {
- info->set_duration(dsp_res->cache_duration());
- }
- if (dsp_res->has_quality()) {
- info->set_ratio(dsp_res->quality());
- }
- info->set_dsp_id(item.dsp_id());
- std::unordered_set<std::string> adids;
- for (auto elem : dsp_res->ad_ids()) {
- adids.insert(elem);
- }
- // do sth
- }
- case 3: // 此 case 为新增
- {
- auto info = dsp_response->add_dsp_res_infos();
- info->set_dsp_id(item.dsp_id());
- if (dsp_res->has_cache_duration()) {
- info->set_duration(dsp_res->cache_duration());
- }
- if (dsp_res->has_quality()) {
- info->set_ratio(dsp_res->quality());
- }
- // do sth
- }
- default:
- break;
- }
发现, 当 dsp_res->bid_type() == 2 的时候, 也会执行 case 3 的部分, 然后尝试在上面各个 "do sth" 后面, 加上 break, 结果符合预期, bug 搞定.
深思
为什么在未增加新 case 之前, test case 能通过呢? 仔细找 QA 问了下 case 的逻辑, 原来, case 每次都会返回 bid_type = 2. 此处, 我们再贴一次之前的代码:
- switch (dsp_res->bid_type()) {
- case 0:
- {
- auto info = dsp_response->add_dsp_res_infos();
- info->set_dsp_id(item.dsp_id());
- if (dsp_res->has_cache_duration()) {
- info->set_duration(dsp_res->cache_duration());
- }
- if (dsp_res->has_quality()) {
- info->set_ratio(dsp_res->quality());
- }
- // do sth
- }
- case 1:
- break;
- case 2:
- {
- auto info = dsp_response->add_dsp_res_infos();
- if (dsp_res->has_cache_duration()) {
- info->set_duration(dsp_res->cache_duration());
- }
- if (dsp_res->has_quality()) {
- info->set_ratio(dsp_res->quality());
- }
- info->set_dsp_id(item.dsp_id());
- std::unordered_set<std::string> adids;
- for (auto elem : dsp_res->ad_ids()) {
- adids.insert(elem);
- }
- // do sth
- }
- default:
- break;
- }
由于 switch 每次都会进入 case 2 的子逻辑, 该逻辑后面就是 default, 然后 break, 没问题. 但是增加了新 case 3 之后, 因为 case 2 和 case 3 后面都没有 break, 导致会把 case 2 和 case 3 的代码都执行了, 直到退出循环或者遇到 break. 此处列下 switch case 的三个规则: switch...case 的三个规则:
既无成功匹配, 又无 default 子句, 那么 swtich 语句块什么也不做;
无成功匹配, 但有 default, 那么 swtich 语句块做 default 语句块的事;
有成功匹配, 没有 break, 那么成功匹配后, 一直执行, 直到遇到 break.
看来我们的线上 bug 是因为遇到了第三个规则导致.
扩展
语句体中不包含 break
- #include <stdio.h>
- int main()
- {
- int iChoice = 0;
- printf("Enter your choice =");
- scanf( "%d",&iChoice);
- switch (iChoice)
- {
- case 1:
- printf("case 1 !\n");
- case 2:
- printf("case 2 !\n");
- case 3:
- printf("case 3 !\n");
- default:
- printf("default !\n" );
- }
- return 0;
- }
当输入 choice 为 1 的时候
当输入 choice 为 2 的时候
原因:
在上面的示例中, 如果 iChoice 等于 1, 则执行主体的所有语句, 因为在开关主体中没有出现 break 语句. 如果 ichoice 等于 2, 则由于没有 break 语句, 因此控制跳至情况 2 并执行以下所有情况.
一个执行语句被多个 case 命中
- void TestFunction(void)
- {
- printf("Demo code\n");
- }
- int main()
- {
- int iChoice = 0;
- printf("Enter your choice =");
- scanf( "%d", &iChoice);
- switch ( iChoice )
- {
- case 1:
- case 2:
- case 3:
- //Calling function
- TestFunction();
- break;
- case 4:
- printf("Wow Test paas !");
- break;
- default:
- printf("default !\n" );
- }
- return 0;
- }
输出为
原因:
对于 case 1 2 3, 都会执行到 TestFunction
存在一样的 case 标签
- #include <stdio.h>
- int main()
- {
- int iChoice = 0;
- int i = 0;
- printf("Enter your choice =");
- scanf( "%d", &iChoice);
- switch ( iChoice )
- {
- case 1:
- i++;
- break;
- case 3:
- i = i + 2;
- break;
- case 3:
- i = i + 3;
- break;
- default:
- printf("default !\n" );
- break;
- }
- printf("Value of i = %d",i);
- return 0;
- }
输出
原因
case 标签不能重复, 否则编译器不能确定进入哪个标签
case 值为浮点数
- #include <stdio.h>
- int main()
- {
- int iChoice = 0;
- int i = 0;
- printf("Enter your choice =");
- scanf( "%d", &iChoice);
- switch (iChoice)
- {
- case 1:
- i++;
- break;
- case 2.5:
- i = i + 2;
- break;
- case 3:
- i = i + 3;
- break;
- default:
- printf("default !\n" );
- }
- printf("Value of i = %d",i);
- return 0;
- }
输出:
原因:
switch 中的参数必须可以转换成一个整数
将 default 语句放在正文的其他地方
- #include <stdio.h>
- int main()
- {
- int iChoice = 0;
- printf("Enter your choice =");
- scanf( "%d", &iChoice);
- switch (iChoice)
- {
- default:
- printf("Bad Input !\n");
- break;
- case 1:
- printf("Your enter choice is 1\n");
- break;
- case 2:
- printf("Your enter choice is 2\n");
- break;
- case 3:
- printf("Your enter choice is 3\n");
- break;
- }
- return 0;
- }
输出:
case 标签值必须为常量
- #include <stdio.h>
- int main()
- {
- int iChoice = 0;
- int Label = 1;
- printf("Enter your choice =");
- scanf( "%d", &iChoice);
- switch (iChoice)
- {
- case Label:
- printf("Your enter choice is 1\n");
- break;
- case 2:
- printf("Your enter choice is 2\n");
- break;
- case 3:
- printf("Your enter choice is 3\n");
- break;
- default:
- printf("Bad Input !\n");
- break;
- }
- return 0;
- }
输出:
嵌套开关
- #include <stdio.h>
- void nestedSwitchDemo(int input1, int input2)
- {
- switch (input1)
- {
- case 1:
- printf("Your enter choice is 1\n");
- switch (input2 )
- {
- case 1:
- printf("Enter Sub choice is 1\n");
- break;
- case 2:
- printf("Enter Sub choice is 2\n");
- break;
- }
- break;
- case 2:
- printf("Your enter choice is 2\n");
- break;
- case 3:
- printf("Your enter choice is 3\n");
- break;
- default:
- printf("Bad Input !\n");
- break;
- }
- }
- int main()
- {
- int iChoice = 1;
- int iSubChoice = 1;
- //Calling function
- nestedSwitchDemo(iChoice,iSubChoice);
- return 0;
- }
输出:
心得
平时编码中, 一定要注意编码规范, 每个 case 都写好对应的 break, 不要学习这种骚操作, 稍不注意就可能出现线上故障.
来源: https://www.qcloud.com/developer/article/1810329