搜索
bottom↓
回复: 59

if switch 函数指针数组 的纠结

[复制链接]

出0入0汤圆

发表于 2014-11-8 12:04:31 | 显示全部楼层 |阅读模式
有一次我同事在中断里面写了一个函数   
void ISR (void)
{
        这里定义了好多局部变量
        if(0==x){}
        else if(1==x) {}
        else if(2==x){}
        else if(3==x){}
        else if(4==x){}
        else if{5==x}{}
        else {}
        。。。。

       
}
我的另外一个同事看到了说 你这样写不如 用switch   说效率高   怎么怎么好
我的同事就改了
void ISR (void)
{
        这里定义了好多局部变量
        switch (x)
        {
                case 0 : {} break;
                case 1 : {} break;
                case 2 : {} break;
                case 3 : {} break;
                case 4 : {} break;
                case 5 : {} break;
                default :{} break;
        }
        。。。。
}

结果系统就跑不起来了   查了半天也没得到结果
我后来也没搞明白   但是我查了一下原因
if 和switch这些分支语句 谁的效率高和低不一定  要看情况   分支少的情况下 if高  多的情况下 switch高
if写出来的代码 占用flash空间多  switch写出来的代码占用flash少  但是会占用ram多   因为if是通过指令代码比较跳转分支
switch是用一个跳转表  这个表是要在栈上分配的  分支多的时候switch通过查表很容易跳转  不需要多次比较
但是这些我想了想  效率还不是最高的  最高的是这样的
function是函数指针数组

void ISR (void)
{
        *function[x](void);

}

我说的不知道你看明白了吗  
我说的对不对 我也不知道
但是问题来了   为什么我同事改写了以后  系统跑不起来了
我的结论 栈溢出了   switch 需要栈很多
到现在  我还是没搞明白

阿莫论坛20周年了!感谢大家的支持与爱护!!

一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。

出0入0汤圆

发表于 2014-11-8 12:47:08 | 显示全部楼层
switch更适合分支有规律的情况,比如你举的例子中都是1,2,3。。。这种情况,分支无规律的话就退化成if-else形式。。。。。所以switch基本是一劳永逸的

出0入0汤圆

发表于 2014-11-8 12:47:21 | 显示全部楼层
编译后,比较一下汇编代码的区别,可能有助于看出问题所在.......

出0入0汤圆

发表于 2014-11-8 12:49:43 | 显示全部楼层
没看出问题所在,等高手。。。。。。

出0入0汤圆

发表于 2014-11-8 12:59:57 | 显示全部楼层
是在单片机,还是在PC上运行的?或者是在嵌入式系统中运行?

不知道switch(x)中的x的类型有没有关系。如果是int型的话,会不会很占用code段。

出0入0汤圆

发表于 2014-11-8 13:13:31 | 显示全部楼层
可以看下汇编代码,应该会看出差别的

出0入0汤圆

发表于 2014-11-8 13:24:38 | 显示全部楼层
可能问题没那么高级,一般用不着考虑这么复杂,就四五个分支,要求也不高,就用switch可读性强。

出0入0汤圆

 楼主| 发表于 2014-11-8 13:31:25 | 显示全部楼层
if switch 函数指针数组 的区别   我纠结的是

出0入0汤圆

发表于 2014-11-8 13:39:02 | 显示全部楼层
分支的我都是用switch,可读性比if好,

出0入12汤圆

发表于 2014-11-8 13:41:10 | 显示全部楼层
自问一下真的在乎省几条指令吗。如果不在乎,那么代码的可读性更未重要。

从你的描绘来看,如果直接将 if 改为 switch 就出问题,那么说明 if 的结果本身代码就已经就不是特别好。

出0入0汤圆

发表于 2014-11-8 13:42:49 | 显示全部楼层
喜欢用 switch

出0入4汤圆

发表于 2014-11-8 13:44:29 来自手机 | 显示全部楼层
switch就不行?

出0入0汤圆

发表于 2014-11-8 13:44:30 来自手机 | 显示全部楼层
应该不是这几个语句的原因吧…

出0入0汤圆

 楼主| 发表于 2014-11-8 13:59:35 | 显示全部楼层
if switch 函数指针数组 的区别   我纠结的是

出0入0汤圆

 楼主| 发表于 2014-11-8 14:01:06 | 显示全部楼层
西施糖葫芦 发表于 2014-11-8 13:44
应该不是这几个语句的原因吧…

if switch 函数指针数组 的区别   我纠结的是

出0入0汤圆

发表于 2014-11-8 14:17:52 | 显示全部楼层
楼上抽搐了?

出0入0汤圆

发表于 2014-11-8 14:48:44 | 显示全部楼层
过程取决你要的结果,如果是要效率,那就要空间换取时间,如果是要省code,那就以时间换取空间,没有绝对的,而且加上优化选项后,不管是用if还是switch,结果可能远非你想象的了,更何况还有cpu指令的差异,其过程有可能天差地的

出0入14汤圆

发表于 2014-11-8 15:09:04 | 显示全部楼层
这种情况switch比较合适,看场所用 分支少if

出0入0汤圆

 楼主| 发表于 2014-11-8 16:52:06 | 显示全部楼层
hyper320 发表于 2014-11-8 14:48
过程取决你要的结果,如果是要效率,那就要空间换取时间,如果是要省code,那就以时间换取空间,没有绝对的,而且 ...

我想请教下  if   switch  的实现机制

出0入0汤圆

发表于 2014-11-9 16:16:38 | 显示全部楼层
我用mdk4.12版 M4 CPU, 优化LEVEL2的状况下测试了一下

uint32_t test(uint32_t num)
{
        uint32_t result ;
       
        result = 0 ;
       
        if(num==1)
        {
                result =1  ;
        } else if(num==2)
        {
                result = 2 ;
        } else if(num==3)
        {
                result =3  ;
        } else if(num==4)
        {
                result = 4 ;
}
       
        return result ;
}

uint32_t test2(uint32_t num)
{
        uint32_t result ;

        result = 0 ;
       
        switch(num)
        {
                case 1:
                        result = 1 ;
                        break ;
                case 2:
                        result = 2 ;
                        break ;
               
                case 3:
                        result = 3 ;
                        break ;
                       
                case 4:
                        result = 4 ;
                        break ;
        }
       
        return result ;
}

在条件小于5个的情况下这两个编译的结果是一模一样,都是用比较的指令一个个比较,当条件超过了4个,switch就用TABLE的指令,而if 还是用比较的方式

所以if和switch到底谁优谁劣其实最终结果还是要看编译器怎么去编译成最后的机械码,也就是汇编

出0入0汤圆

发表于 2014-11-9 16:27:48 | 显示全部楼层
如果switch用了table表指令,那消耗的时间是一模一样的,如果用比较指令,那执行时间就看运气了,如果比较的是最后一个,就表示要通过前面n个比较,所以比较耗时
而用程序数组调用方式应该比table指令稍慢,因为还要呼叫副程序,要压栈退栈,并且要消耗指标空间.消耗堆栈空间

出0入0汤圆

发表于 2014-11-9 18:05:00 | 显示全部楼层
楼主先要把switch的分枝减少试下,把堆栈溢出的情况排除掉

出0入0汤圆

 楼主| 发表于 2014-11-10 10:41:26 | 显示全部楼层
hyper320 发表于 2014-11-9 16:27
如果switch用了table表指令,那消耗的时间是一模一样的,如果用比较指令,那执行时间就看运气了,如果比较的是 ...

谢谢你的热心回答

出0入0汤圆

发表于 2014-11-10 11:04:15 | 显示全部楼层
函数指针也不是查表跳转吗?一般这种情况就是用case的。

出0入4汤圆

发表于 2014-11-10 11:12:01 | 显示全部楼层
可以将编译器中的优化去掉试试

出0入0汤圆

发表于 2014-11-10 11:18:42 | 显示全部楼层
学习了~楼主可以尝试下楼上说的~看把编译优化项去掉,对比下

出0入0汤圆

发表于 2014-11-10 11:25:10 | 显示全部楼层
喜欢用switch可读性好

出0入96汤圆

发表于 2014-11-10 11:45:30 | 显示全部楼层
case 0 : {} break;这里是不用加大括号括起来的,你试试去掉{}

出0入0汤圆

发表于 2014-11-10 11:52:43 | 显示全部楼层
分支多我用switch,可读性比if好,简单判断用IF

出0入0汤圆

发表于 2014-11-10 12:06:09 | 显示全部楼层
hyper320 发表于 2014-11-9 16:16
我用mdk4.12版 M4 CPU, 优化LEVEL2的状况下测试了一下

uint32_t test(uint32_t num)

Mark学习。

出0入0汤圆

发表于 2014-11-10 12:06:33 | 显示全部楼层
我也觉得swith的可读性比较好,

出0入93汤圆

发表于 2014-11-10 12:21:17 | 显示全部楼层
switch的可读性好很多,效率则不一定。
switch的局限性比if要多得多。
if和switch不是完全等价的,if是具有优先顺序的,而switch则不是。
就算switch要用到跳转表(根据编译器策略可能会用,也可能不用),它也不会傻呵呵的分配到栈上去,而是分配在常量区。
就你所说的函数指针数组,效率只会比switch差。switch生成跳转指令,函数指针要生成函数调用返回,至少PC进栈出栈就是多余的。

出0入0汤圆

 楼主| 发表于 2014-11-10 14:17:26 | 显示全部楼层
takashiki 发表于 2014-11-10 12:21
switch的可读性好很多,效率则不一定。
switch的局限性比if要多得多。
if和switch不是完全等价的,if是具有 ...

谢谢指导

出0入0汤圆

发表于 2014-11-10 14:19:31 | 显示全部楼层
takashiki 发表于 2014-11-10 12:21
switch的可读性好很多,效率则不一定。
switch的局限性比if要多得多。
if和switch不是完全等价的,if是具有 ...

我测试了下switch,的确是没有顺序性,测试的结果switch似乎是按照由小到大作比较的,这细节以前还真没注意过,谢谢这位童鞋的提示

出0入296汤圆

发表于 2014-11-11 08:16:12 来自手机 | 显示全部楼层
hyper320 发表于 2014-11-10 14:19
我测试了下switch,的确是没有顺序性,测试的结果switch似乎是按照由小到大作比较的,这细节以前还真没注意 ...

这条不能作为经验记录下来,这是与编译器高度相关的。在经验中学习很危险。

出0入0汤圆

 楼主| 发表于 2014-11-11 09:46:49 | 显示全部楼层
Gorgon_Meducer 发表于 2014-11-11 08:16
这条不能作为经验记录下来,这是与编译器高度相关的。在经验中学习很危险。 ...

既然版主都来了   还是指点下   让我学习下

出0入20汤圆

发表于 2014-11-11 10:00:47 | 显示全部楼层
大量分支肯定用SWITCH,少量用IF。现在C编程,不要再考虑什么效率了。可维护才是正道。

出0入0汤圆

发表于 2014-11-11 13:35:29 | 显示全部楼层
本帖最后由 hyper320 于 2014-11-11 13:38 编辑
Gorgon_Meducer 发表于 2014-11-11 08:16
这条不能作为经验记录下来,这是与编译器高度相关的。在经验中学习很危险。 ...


认为switch”可能会不顺序”会比”顺序”的安全吧,起码在写作的时候,至少不会让写作的人误判,怎会没有参考价值呢,如果其他的编译器编译出来if没有顺序性那才是编译器高度相关的吧
起码我现在写的时候都是用if有顺序性为前提去写作的,如果if没有顺序性那么就可能发生不可预知的情况了

出0入296汤圆

发表于 2014-11-12 19:11:52 | 显示全部楼层
hyper320 发表于 2014-11-11 13:35
认为switch”可能会不顺序”会比”顺序”的安全吧,起码在写作的时候,至少不会让写作的人误判,怎会没有参 ...

当编译器没有规定的时候不可以做假设。这是我想说的。其实switch对程序逻辑来说是
有顺序性的,fall through写法就是基于这个特性。protoThread利用的就是这个顺序性。
但这是从逻辑上说的,如果编译器判定你所有分支都加了break,在高度优化的情况下
顺序性就得不到保证了。尤其是'IAR环境。所以说,写代码不能依赖这些经验,最好
告诉自己,只要自己代码不依赖某些特性就行,其他其实知道不知道都无所谓。

出0入296汤圆

发表于 2014-11-12 19:15:25 | 显示全部楼层
shinemotou 发表于 2014-11-11 09:46
既然版主都来了   还是指点下   让我学习下

这个问题在我状态机的帖子里面('ARM板块)早就讨论的非常深入,充分进行
汇编级别的剖析,并且给出了结论和有话方法。建议大家直接去看那个帖子就好。

出0入296汤圆

发表于 2014-11-12 19:22:00 | 显示全部楼层
hyper320 发表于 2014-11-11 13:35
认为switch”可能会不顺序”会比”顺序”的安全吧,起码在写作的时候,至少不会让写作的人误判,怎会没有参 ...

http://www.amobbs.com/thread-5507175-1-1.html
从85楼开始

出0入0汤圆

发表于 2014-11-12 21:45:20 | 显示全部楼层
Gorgon_Meducer 发表于 2014-11-12 19:11
当编译器没有规定的时候不可以做假设。这是我想说的。其实switch对程序逻辑来说是
有顺序性的,fall thro ...


C接触的很早,但真正是这两年才开始应用的,之前都是写汇编居多,很习惯的我都会把C编译的结果汇编对照一下,所以在写C的时候我都不会把他当做理所当然,像if也是后来才知道有顺序性的,所以一开始为了确定起见都是采取第一种方式,后来才改写成第二种方式;

第一种:
If(x==a)
{
...
}

If(y==b)
{
...
}


第二种:
If(x==a && y==b)
{
...
}

而采取siwtch时我也都没有预设他会不会照顺序执行,因为当条件有独立性,所差的只是执行时间,而不会引响到程序的逻辑性,除非是位相关的比较,例如 条件为 bit0, bit0 & bit1, bit0 & bit2,转成判断为 3,1,5(假设判别顺序是这样)

第三种:
Switch(num)
{
Case 3:
..
Break ;

Case 1:
..
break ;

Case 5:
..
break ;
}

第四种:
If(num==3)
{
..
}
else if(num==1)
{
..
}
else if(num==5)
{
..
}

那么这时条件的判断可能有顺序性,在我不确定switch的特性时,我绝不会用switch的方式,也就是第三种,而是改用 if 照当时条件的顺序性来判别(第四种0!
我了解版主的用心,是不希望大家把它当成理所当然,除非清楚的知道指令的前因后果,本身对硬件逻辑概念还匴可以,而硬件就有很强的逻辑性,转为软件考虑点会更多,所以不会去犯这种低级错误的!!多谢版主的谨慎关心!!

出0入0汤圆

发表于 2014-11-12 22:13:49 | 显示全部楼层
void ISR (void)
{
        if(0==x)
          {x = 2;}
        else if(1==x) {}
        else if(2==x){ fun_a();}
        else if(3==x){}
        else if(4==x){}
        else if{5==x}{}
        else {}
        。。。。

        
}

void ISR (void)
{
        这里定义了好多局部变量
        switch (x)
        {
                case 0 :
                           {
                           x=2;
                           } break;
                case 1 : {} break;
                case 2 : {fun_b();} break;
                case 3 : {} break;
                case 4 : {} break;
                case 5 : {} break;
                default :{} break;
        }
}
这样比较一下,在第一个if(x==0)后,会执行fun_a()吗
在switch分支里面,会执行fun_b()吗?

出0入0汤圆

发表于 2014-11-12 22:22:26 | 显示全部楼层
dzymushi 发表于 2014-11-12 22:13
void ISR (void)
{
        if(0==x)


两个都不会,因为已经到达结果阶段,直接跳出if else了,影响的是下一次的比较

出0入0汤圆

发表于 2014-11-12 22:37:10 | 显示全部楼层
我用switch多一点

出0入0汤圆

发表于 2014-11-12 22:38:06 | 显示全部楼层
hyper320 发表于 2014-11-12 22:22
两个都不会,因为已经到达结果阶段,直接跳出if else了,影响的是下一次的比较 ...

我用switch多一点

出0入0汤圆

发表于 2014-11-12 23:01:59 | 显示全部楼层
我以前也是用IF多一点,但是现在都是用switch了

出0入296汤圆

发表于 2014-11-13 00:16:37 | 显示全部楼层
本帖最后由 Gorgon_Meducer 于 2015-1-8 10:55 编辑
hyper320 发表于 2014-11-12 21:45
C接触的很早,但真正是这两年才开始应用的,之前都是写汇编居多,很习惯的我都会把C编译的结果汇编对照一下, ...


编译器不同,版本不同,优化选项不同,常数的选取方式不同,最后结果都有可能不同。
我觉得从不确定事情的经验中学习不是太可靠。要记忆的东西太多,要写可移植性高的代码
就限制了很多技巧的应用和依赖。就switch来说,如果你用了fall through,顺序性会得到
编译器的强行保证,因为这是语法规定的,如果你不使用fall through,真的就看具体情况了

你对一款芯片在特定的编译器的你熟悉的有话选项下的行为很熟悉,这无可厚非。我只是
接着你的评论希望大家知道你下结论的前提是你很熟悉的环境下就事论事的。我本无冒犯
的意思,语气上让你误会了。在这里我向你道歉。

我想强调的是,看客们如果想写移植性高的代码,就不应该让代码依赖于某些经验。因为
经验是你在特定化境下获得的,并不一定放之四海而皆准。不要在经验中学习,而应该
从经验中总结和思考。

关于switch和if在iar环境下,如果开最大尺寸优化,当switch判断的数值不是从零开始的
连续整数时,if的效率和switch基本相同或者略好一点(有时候差2个字节左右);当switch
判断的数值是从零开始的连续整数时,switch的尺寸较小,判断的分支越多,差别越大。
当使用fall through时,switch会确保顺序性,反之则不一定。

出0入296汤圆

发表于 2014-11-13 00:20:02 | 显示全部楼层
dzymushi 发表于 2014-11-12 22:13
void ISR (void)
{
        if(0==x)

你问的部分这是C基本语法,你应该去看语法书而不是靠问别人。

出0入0汤圆

发表于 2014-11-13 01:16:41 | 显示全部楼层
Gorgon_Meducer 发表于 2014-11-13 00:16
编译器不同,版本不同,优化选项不同,常数的选取方式不同,最后结果都有可能不同。
我觉得从不确定事情 ...

客气了,我也是个追根究底的人,因为这样才能知其然而之所以然,之前也常常遇到有人问说这个指令万一不影响标志位怎么办,听到这种话的时候我有些无语,大家都知道指令如何运行都是有规则的,如果脱出了规则,那ic就变的不可信,cpu一秒执行千万个指令,只要一个出错可能全盘皆错,所以大部分的时候都是使用者自己逻辑出错而不是cpu有问题,版主看起来是科班出身的,实事求是,我很认同这种态度,其实讨论问题的碰撞可能擦出许多火花,不管是好是坏都是求是,在与人交徃可以随便,但跟机器打交道就必须实事求是,,当然我在高阶语言造诣上比不上版主,很多地方只能先怀疑以最没有问题的方式解决,慢慢从中获取一些相关知识,改进之后的写作,希望版主以后多指教,教学相长!!

出0入0汤圆

 楼主| 发表于 2014-11-13 17:53:04 | 显示全部楼层
hyper320 发表于 2014-11-13 01:16
客气了,我也是个追根究底的人,因为这样才能知其然而之所以然,之前也常常遇到有人问说这个指令万一不影响 ...

只有探讨的深入  才能让人思考进步  谢谢你的参与

出0入0汤圆

发表于 2015-1-8 10:32:21 | 显示全部楼层
Gorgon_Meducer 发表于 2014-11-13 00:16
编译器不同,版本不同,优化选项不同,常数的选取方式不同,最后结果都有可能不同。
我觉得从不确定事情 ...

好的 学习了

出0入0汤圆

发表于 2015-1-8 10:39:14 | 显示全部楼层
用if写的播放器核心代码比用switch音质要好很多。。。

出0入0汤圆

发表于 2015-1-8 10:43:00 | 显示全部楼层
可以试试   分支过多的情况   程序会不会出现问题。。。

出0入0汤圆

发表于 2015-1-8 10:57:38 | 显示全部楼层
楼主的现象应该是栈溢出了。分支少有序喜欢用if else 反之喜欢switch

出0入0汤圆

发表于 2015-3-7 20:32:38 | 显示全部楼层
我想问一下,楼主找到原因了吗?为什么用SWITCH会没有结果?

出0入0汤圆

发表于 2015-3-7 20:43:44 | 显示全部楼层
楼主问题单步跑应当能找出

出0入0汤圆

发表于 2015-3-7 20:56:20 | 显示全部楼层
cheungman 发表于 2015-1-8 10:39
用if写的播放器核心代码比用switch音质要好很多。。。

能举个例子吗!

出0入0汤圆

发表于 2015-3-7 21:31:59 | 显示全部楼层
跑测试用例,或者看汇编就知道哪种效率高了

出0入0汤圆

发表于 2015-8-24 19:39:31 | 显示全部楼层
学习了,好文!
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-6-17 21:51

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表