跟我学C++中级篇——if条件语句与switch比较

news2024/9/28 1:22:37

一、背景介绍

在前面的一些分析介绍以及网上的资料或者书籍中,都有说过如果条件分支语句较多时,推荐使用switch而不要使用if语句,因为前者的速度比后者要快,特别是数量多时,会比较明显。但其实这句话是有问题的。在经常写类似条件语句的开发者眼中看来,可能很少考虑二者到底有什么不同,心中有一个大概的印象就可以了。真正开发时,很少有这种较真的场景。
答案确实也是如此。但正因为这样,大多数的开发者往往忽略这个问题或者觉得它不值一哂。但学习的过程不能放过每一个细节,可以不对其重视,但不能不知道其所以然。

二、if和switch的处理

一般来说,传统的认知中可能是下面的想法:
1、if会对条件进行反复不断的比较来最终确定跳转的分支语句
2、switch则会是查一个表来进行跳转,所以它的效率会高
但事实一定是这样么?
整体上来言,上面的下意识的想法不能说错误,只能说不完全正确。但是实际上编译器的处理还是有一定的复杂性的:
1、编译器优化的情况下,二者没有差别的。所以,在前面反复提到过不要小看编译器的进步
2、即使是在没有开优化的前提下,如果值的内容有所不同,也有可能产生一些意想不到的情况。比如值比较分散,则编译器对switch使用二分查找方式,如果比较连续,则使用无冲突的散列表来进行处理。
3、在非常少的比较时,二者几乎也没有什么差距,比如只有两个分支
看一下这个例子:

#include <iostream>

using namespace std;
void testIf(int d) {
  if (d == 1) {
    std::cout << "call 1 !" << std::endl;
  } else if (d == 2) {
    std::cout << "call 2 !" << std::endl;
  } else if (d == 3) {
    std::cout << "call 3 !" << std::endl;
  } else if (d == 4) {
    std::cout << "call 4 !" << std::endl;
  } else if (d == 5) {
    std::cout << "call 5 !" << std::endl;
  } else if (d == 6) {
    std::cout << "call 6 !" << std::endl;
  } else {
    std::cout << "call default !" << std::endl;
  }
}
void testSwitch(int d) {
  switch (d) {
  case 1:
    std::cout << "call 1 branch!" << std::endl;
    break;
  case 2:
    std::cout << "call 2 branch!" << std::endl;
    break;
  case 3:
    std::cout << "call 3 branch!" << std::endl;
    break;
  case 4:
    std::cout << "call 4 branch!" << std::endl;
    break;
  case 5:
    std::cout << "call 5 branch!" << std::endl;
    break;
  case 6:
    std::cout << "call 6 branch!" << std::endl;
    break;
  default:
    std::cout << "call default branch!" << std::endl;
    break;
  }
}
int main() {
  int d = 3;
  testSwitch(d);
  testIf(d);
  return 0;
}

它的反汇编代码可以看出不同来:

//testIF:
.LC0:
        .string "call 1 !"
.LC1:
        .string "call 2 !"
.LC2:
        .string "call 3 !"
.LC3:
        .string "call 4 !"
.LC4:
        .string "call 5 !"
.LC5:
        .string "call 6 !"
        
_Z6testIfi:
.LFB1731:
	.cfi_startproc
	endbr64
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	subq	$16, %rsp
	movl	%edi, -4(%rbp)
	cmpl	$1, -4(%rbp)
	jne	.L2
	leaq	.LC0(%rip), %rax
	movq	%rax, %rsi
	leaq	_ZSt4cout(%rip), %rax
	movq	%rax, %rdi
	call	_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@PLT
	movq	_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_@GOTPCREL(%rip), %rdx
	movq	%rdx, %rsi
	movq	%rax, %rdi
	call	_ZNSolsEPFRSoS_E@PLT
	jmp	.L9

.L2:
        cmpl    $2, -4(%rbp)
        jne     .L4
        leaq    .LC1(%rip), %rax
        movq    %rax, %rsi
        leaq    _ZSt4cout(%rip), %rax
        movq    %rax, %rdi
        call    _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@PLT
        movq    _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_@GOTPCREL(%rip), %rdx
        movq    %rdx, %rsi
        movq    %rax, %rdi
        call    _ZNSolsEPFRSoS_E@PLT
        jmp     .L9
.L4:
        cmpl    $3, -4(%rbp)
        jne     .L5
        leaq    .LC2(%rip), %rax
        movq    %rax, %rsi
        leaq    _ZSt4cout(%rip), %rax
//testSwitch:
.LC7:
        .string "call 1 branch!"
.LC8:
        .string "call 2 branch!"
.LC9:
        .string "call 3 branch!"
.LC10:
        .string "call 4 branch!"
.LC11:
        .string "call 5 branch!"
_Z10testSwitchi:
.LFB1732:
	.cfi_startproc
	endbr64
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	subq	$16, %rsp
	movl	%edi, -4(%rbp)
	cmpl	$6, -4(%rbp)
	ja	.L11
	movl	-4(%rbp), %eax
	leaq	0(,%rax,4), %rdx
	leaq	.L13(%rip), %rax
	movl	(%rdx,%rax), %eax
	cltq
	leaq	.L13(%rip), %rdx
	addq	%rdx, %rax
	notrack jmp	*%rax
.L18:
        leaq    .LC7(%rip), %rax
        movq    %rax, %rsi
        leaq    _ZSt4cout(%rip), %rax
        movq    %rax, %rdi
        call    _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@PLT
        movq    _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_@GOTPCREL(%rip), %rdx
        movq    %rdx, %rsi
        movq    %rax, %rdi
        call    _ZNSolsEPFRSoS_E@PLT
        jmp     .L19
.L17:
        leaq    .LC8(%rip), %rax
        movq    %rax, %rsi
        leaq    _ZSt4cout(%rip), %rax
        movq    %rax, %rdi
        call    _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@PLT
        movq    _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_@GOTPCREL(%rip), %rdx
        movq    %rdx, %rsi
        movq    %rax, %rdi
        call    _ZNSolsEPFRSoS_E@PLT
        jmp     .L19

从上面的汇编代码中可以发现,在if分支处理中,每次都要进行比较cmpl动作,而在switch中则只在开始进行了比较然后就是一个notrack jmp。其实编译器比想象的这些还要复杂,比如值得连续性到何种程度使用何种形式的表,多么的分散算可以使用二分查找。
一般来说,Case间隔值大于256时,可能会生成一种类似树的结构,使用二分查找;如果是连续的值,则会生成一个大的散列表,然后直接跳转;如果是连续的值中有一些间隔,可能是使用一个大的散列表再加一个小的散列表的方法,防止内存浪费,直接跃过一些无效的Case;而在很少的情况下,等同于if分支语句。
这些都可以自行测试一下。不过,不同的编译器可能有所不同。

三、整体的分析

可以从两咱形式上考虑问题:
1、形式上考虑,主要是对使用二者在上层开发者的开发习惯、维护等考虑,这种情况下一般对效率不是太敏感或者说二者的效率没有特别的差距:
if条件分支语句更接近于人类的普通认知,所以一般三个以下的分支判断建议还是用if来处理。这样更容易维护,而且也不是所有的场景下都需要快速的跳转,所以此时就看整体的开发者的控制了。而在五个及以上时,建议使用switch,一个是更容易优化,另外也容易处理一些通用逻辑。当然,switch太多也让人心烦。在某些场景下见过上千个以上的Case,这还是相当要命的。另外,如果if条件分支语句可以避免嵌套if分支时,也建议使用switch,比如用switch来处理多个条件使用相同的处理逻辑时,如果用if可能会进行嵌套处理,就不如前者容易维护和理解。
2、技术上考虑
如果开启了编译器优化,则开发者对此几乎是无法控制了。编译器会根据自己的喜好来处理最终的编译结果。一般情况下,实际开发者很难遇到十个以上的条件判断并同时需要效率的强需求情况。象网上那种几百个条件分支和判断再嵌套多少层的。这就不是单纯的技术问题了,这是设计有问题。所以,除了在一些效率要求特别强(比如航天或者说实时系统等)的场景下,大家还是根据自己的情况处理即可,不用刻意控制。

四、总结

细节的重要程度看开发者所处的环境。实际的需求才是对细节把握程度的要求点,开发者不必为每一个细节焦虑,这个没有意义。要学会抓大放小,能收能放,才能更好的解决实际问题。还是那句话,要重视细节但不要陷入细节。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2171793.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ArduSub程序学习(11)--EKF实现逻辑①

1.read_AHRS() 进入EKF&#xff0c;路径ArduSub.cpp里面的fast_loop()里面的read_AHRS(); //从 AHRS&#xff08;姿态与航向参考系统&#xff09;中读取并更新与飞行器姿态有关的信息 void Sub::read_AHRS() {// Perform IMU calculations and get attitude info//----------…

WinForm程序嵌入Web网页

文章目录 前言一、三方库或控件的选择测试二、Microsoft Edge WebView2安装、使用步骤1.安装2.使用 前言 由于此项目需要winform客户端嵌入web网页并于JAVA端交互数据&#xff0c;所以研究了一下嵌入web网页这部分&#xff0c;趟了一遍雷&#xff0c;这里做下记录。 一、三方库…

软件设计之Maven(2)

软件设计之Maven(2) 路线图推荐&#xff1a; 【Java学习路线-极速版】【Java架构师技术图谱】 尚硅谷新版Maven教程&#xff08;高效入门maven&#xff0c;上手又快又稳&#xff09; 资料可以去尚硅谷官网免费领取 学习内容&#xff1a; 依赖管理版本统一及维护依赖范围Buil…

Comfyui 学习笔记1

如果图像输出被裁剪&#xff0c;则需要使用PrepImageForClipVision&#xff0c;来设置图像距离上边沿的位置. 决定绘画的作用区域&#xff0c;后面的KSample只作用到 mask标记的范围。 图像位置偏移了&#xff0c;可以考虑通过Image crop 裁剪 IPAdapter face 提取时&…

基于Python大数据可视化的白酒数据推荐及数据分析系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码 精品专栏&#xff1a;Java精选实战项目…

Java并发:互斥锁,读写锁,公平锁,Condition,StampedLock

阅读本文之前可以看一看 Java 多线程基础&#xff1a; Java&#xff1a;多线程&#xff08;进程线程&#xff0c;线程状态&#xff0c;创建线程&#xff0c;线程操作&#xff09; Java&#xff1a;多线程&#xff08;同步死锁&#xff0c;锁&原子变量&#xff0c;线程通信&…

《Linux从小白到高手》理论篇(二):Linux的目录结构和磁盘管理

List item 本篇主要介绍Linux的目录结构和磁盘管理相关的命令。 Linux目录结构 在Linux的世界里&#xff0c;一切皆文件&#xff0c;连目录也不例外。linux的文件系统是采用级层式的树状目录结构&#xff0c;在此结构中的最上层是根目录“/”&#xff08;相当于windows下的&…

【VUE】案例:商场会员管理系统

编写vuedfr实现对会员进行基本增删改查 1. drf项目初始化 请求&#xff1a; POST http://127/0.0.0.1:8000/api/auth/ {"username":"cqn", "password":"123"}返回&#xff1a; {"username":"cqn", "token&q…

开源 AI 智能名片 O2O 商城小程序与抖音:品牌传播的新机遇与挑战

摘要&#xff1a;本文探讨了开源 AI 智能名片 O2O 商城小程序在品牌传播中的作用&#xff0c;以及与抖音平台相结合所带来的机遇与挑战。分析了抖音如何利用算法适配品牌调性为门店找到目标消费者&#xff0c;放大品牌势能&#xff0c;同时阐述了新品牌在抖音上进行品牌传播的优…

后端返回内容有换行标识,前端如何识别换行

<br/>的话 用 v-html \n 可以用css样式 white-space: pre-wrap 后端返回结果 前端

集成电路发展的两条主线

集成电路发展的两条主线&#xff0c; 1、增大晶圆尺寸&#xff0c;6、8、12英寸 晶圆尺寸不断增大&#xff0c;其设备要求不断增大。目前主流的8英寸&#xff0c;12英寸正在发展 2、芯片工艺制程不断减小

基于Hive和Hadoop的用电量分析系统

本项目是一个基于大数据技术的用电量分析系统&#xff0c;旨在为用户提供全面的电力消耗信息和深入的用电量分析。系统采用 Hadoop 平台进行大规模数据存储和处理&#xff0c;利用 MapReduce 进行数据分析和处理&#xff0c;通过 Sqoop 实现数据的导入导出&#xff0c;以 Spark…

quill富文本插入表格quill-better-table

使用quill-better-table插件&#xff0c;官网GitCode - 全球开发者的开源社区,开源代码托管平台 安装 首先quill-better-table插件&#xff0c;官网有写需要 quills v2.0.0-dev3 我这里使用的是 quills v2.0.0-dev4&#xff0c;自行安装 然后就是安装我们的插件 quill-bett…

【项目资料】项目售后服务方案(Word)

1.1 售后服务方案 概述 售后服务体系 售后服务流程 售后服务承诺 售后服务计划 技术支持响应承诺 售后服务响应时间 1.2 项目培训方案 项目培训体系 项目培训管理 培训目的与措施 项目培训安排 培训告知下达 培训人员贯彻 培训签到表 软件全套资料部分文档清单&#xff1a; 工作…

10款超好用的文档加密软件|2024企业文档加密软件分享

在信息安全日益受到重视的今天&#xff0c;选择合适的文档加密软件至关重要。以下是2024年值得关注的10款超好用的文档加密软件&#xff0c;帮助企业提升数据安全性。 1. Ping32文档加密软件 Ping32专注于文档加密&#xff0c;采用强大的AES加密技术&#xff0c;确保敏感信息在…

领夹式无线麦克风哪个品牌好,口碑最好的领夹麦克风品牌推荐

对于追求专业音频录制的创作者而言&#xff0c;无线领夹麦克风不仅是录制设备&#xff0c;更是表达自我的艺术工具。从市场反馈中&#xff0c;我们发现西圣、大疆、罗德等品牌深受消费者喜爱。不过我看事情不能只看一面&#xff0c;在市场繁荣的表象下&#xff0c;一些劣质产品…

单片机的两种看门狗原理解析——IWDG和WWDG

一、IWDG独立开门狗的主要性能 计时机制&#xff1a; 递减计数器 独立开门狗的初始频率&#xff1a; LSI低速内部时钟&#xff1a;RC震荡器&#xff0c;40kHz 独立开门狗是以LSI为初始频率的&#xff0c;所以独立开门狗的初始时钟频率取决与单片机本身&#xff0c;因此在使…

Charles(青花瓷)抓取https请求

文章目录 前言Charles&#xff08;青花瓷&#xff09;抓取https请求 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&#xff0c;写作不易啊^ _ ^。   而且听说点赞的人每天的运气都不会太差&#xff0c;实在白嫖的话&…

【工具分享】Chimera勒索病毒解密工具

前言 Chimera勒索软件首次出现在2015年&#xff0c;最初是在德国被发现。该勒索软件不仅加密受害者的文件&#xff0c;还威胁如果不支付赎金&#xff0c;就会将被盗的数据公开发布。这种“双重勒索”策略使得Chimera在众多勒索软件中脱颖而出。Chimera通常通过钓鱼邮件传播&am…

第五部分:6---信号的递达

目录 信号的递达流程&#xff1a; 信号在什么时候递达&#xff1f; 用户态和内核态&#xff1a; 内核态、用户态在页表的映射关系&#xff1a; 操作系统如何得知当前执行状态是用户态还是内核态&#xff1f; 操作系统如何处理被捕捉的信号&#xff1f; 信号的递达流程&am…