如果使用else子句构造else,那么终止的好处是什么?
我们的组织有一个必要的编码规则(没有任何解释):
如果… else如果构造应该用else子句终止
例1:
if ( x < 0 ) { x = 0; } /* else not needed */
例2:
if ( x < 0 ) { x = 0; } else if ( y < 0 ) { x = 3; } else /* this else clause is required, even if the */ { /* programmer expects this will never be reached */ /* no change in value of x */ }
这个devise要处理什么边缘情况?
关于这个原因,我还担心的是, 例子1不需要else
只是例子2 。 如果原因是可重用性和可扩展性,我认为应该在这两种情况下使用。
正如另一个答案中提到的,这是来自MISRA-C编码准则。 其目的是防御性编程,这是一个常用于任务关键型编程的概念。
也就是说,每个if - else if
必须以else
结束,并且每个switch
必须以default
结束。
有两个原因:
-
自我logging的代码。 如果你写了一个
else
但把它留空,这意味着:“我已经明确地考虑过这个情况,if
不是else if
是真的else if
也是如此”。这里没有写一个
else
意思:“要么我考虑过这种情况,if
这两种情况都不是这样,或者我完全忘记了考虑,那么我的代码中就有可能是一个胖子。” -
停止失控的代码。 在关键任务软件中,您需要编写function强大的程序,即使对于极不可能的程序也是如此。 所以你可以看到类似的代码
if (mybool == TRUE) { } else if (mybool == FALSE) { } else { // handle error }
这个代码对PC程序员和计算机科学家来说是完全陌生的,但是它在任务关键型软件中是非常有意义的,因为它捕捉到“mybool”不pipe出于什么原因都已经腐败的情况。
从历史上看,由于EMI /噪声,您会担心RAM存储器的损坏。 今天这不是什么大问题。 更有可能的是,由于代码中其他地方的错误而发生内存损坏:指向错误位置的指针,数组越界错误,堆栈溢出,失控代码等等。
所以大多数情况下,当你在实现阶段写出错误时,这样的代码就会回到自己的面前。 这意味着它也可以用作debugging技术:你正在编写的程序告诉你什么时候写了错误。
编辑
关于为什么以后不需要每个单一的, if
:
if-else
或if-else if-else
完全覆盖variables可能具有的所有可能的值。 但是, if
一个普通的陈述不一定涵盖了所有可能的价值,它就有了更广泛的用法。 大多数情况下,你只是想检查一定的条件,如果不符合,则什么也不做。 那么编写防御性编程来覆盖else
情况根本就没有意义。
另外,如果你在每个else
后都写了一个空的else
,那么它会把代码完全搞乱。
MISRA-C:2012 15.7没有给出为什么不需要的理由,它只是说:
注意:简单的
if
语句不需要finalelse
语句。
你的公司遵循MISRA编码指导。 这些准则的几个版本包含这个规则,但是来自MISRA-C:2004 † :
规则14.10(必需):如果… else所有结构都应该以else子句结束。
只要if语句后跟一个或多个if语句,此规则就适用; 最后
if
其他的if
应该跟着一个else
语句。 在简单的if
语句的情况下,else
语句不需要被包含。 最后的陈述的要求是防御性编程。else
语句要么采取适当的行动,要么包含适当的评论,为什么不采取行动。 这与switch
语句中具有最终default
子句的要求是一致的。 例如这个代码是一个简单的if语句:if ( x < 0 ) { log_error(3); x = 0; } /* else not needed */
而下面的代码演示了
if
,else if
结构if ( x < 0 ) { log_error(3); x = 0; } else if ( y < 0 ) { x = 3; } else /* this else clause is required, even if the */ { /* programmer expects this will never be reached */ /* no change in value of x */ }
在MISRA-C:2012取代2004版本,是目前对新项目的build议, 存在相同的规则,但编号为15.7 。
示例1:在单个if语句中,程序员可能需要检查n个条件并执行单个操作。
if(condition_1 || condition_2 || ... condition_n) { //operation_1 }
在常规的使用中, if
使用的话,执行一个操作是不需要的。
示例2:这里程序员检查n个条件并执行多个操作。 在正常使用if..else if
是像switch
你可能需要执行一个像默认的操作。 所以按照misra的标准,需要使用else
if(condition_1 || condition_2 || ... condition_n) { //operation_1 } else if(condition_1 || condition_2 || ... condition_n) { //operation_2 } .... else { //default cause }
†这些出版物的当前版本和过去版本可以通过MISRA网站 ( via )购买。
这相当于在每个交换机中都要求默认情况。
这额外的将减less你的程序的代码覆盖率 。
根据我的经验,将linux内核或android代码移植到不同的平台上,很多时候我们做了一些错误的事情,在logcat中,我们看到一些错误
if ( x < 0 ) { x = 0; } else if ( y < 0 ) { x = 3; } else /* this else clause is required, even if the */ { /* programmer expects this will never be reached */ /* no change in value of x */ printk(" \n [function or module name]: this should never happen \n"); /* It is always good to mention function/module name with the logs. If you end up with "this should never happen" message and the same message is used in many places in the software it will be hard to track/debug. */ }
只是一个简单的解释,因为我在5年前就这样做了。
有(大多数语言)没有语法要求,包括“空” else
语句(和不必要的{..}
),在“简单的小程序”,没有必要。 但真正的程序员不会编写“简单的小程序”,同样重要的是,他们不会编写将被使用一次然后被丢弃的程序。
当你写一个if / else时:
if(something) doSomething; else doSomethingElse;
这一切似乎都很简单,甚至几乎看不到添加{..}
。
但有一天,几个月后,其他一些程序员(你永远不会犯这样的错误!)将需要“增强”程序,并添加一个声明。
if(something) doSomething; else doSomethingIForgot; doSomethingElse;
突然之间,有些else
忘记了它应该是在另一边。
所以你是一个很好的小程序员,你总是使用{..}
。 但是你写道:
if(something) { if(anotherThing) { doSomething; } }
一切都很好,直到那个新的孩子做了一个午夜的修改:
if(something) { if(!notMyThing) { if(anotherThing) { doSomething; } else { dontDoAnything; // Because it's not my thing. }} }
是的,它的格式不正确,但项目中的代码也是一半,“自动格式化程序”被所有#ifdef
语句混淆了。 当然,真正的代码比这个玩具的例子要复杂得多。
不幸的是(或者不是),我已经有几年没有这样的事情了,所以我没有一个新的“真实”的例子 – 上面(显然)是人为的,有点狡猾。
这样做是为了使代码更具可读性,以供稍后参考,并向后来的审稿人说明,由else
处理的其余案件是不做任何事情的 ,所以它们在某种程度上不会被忽视。
这是一个很好的编程习惯,使得代码可以重用和扩展 。
我想补充一些前面的回答,部分是相反的。 虽然如果以开关式的方式使用if-else是很常见的,这种方式应该涵盖expression式的全部可想象的值,但是不能保证全部覆盖任何可能的条件范围。 关于交换结构本身也是如此,因此要求使用一个默认的子句,这个子句可以捕获所有剩余的值,如果没有其他要求的话,也可以用作断言保护。
这个问题本身就有一个很好的反例:第二个条件根本不涉及x(这就是为什么我经常更喜欢基于开关的变体比较灵活的基于if的变体)。 从例子中可以看出,如果满足条件A,则应将x设置为某个值。 如果A不符合,则条件B被testing。 如果满足,则x应该得到另一个值。 如果A和B都不符合,那么x应保持不变。
这里我们可以看到一个空白的分支应该用来评论程序员对读者的意图。
另一方面,我不明白为什么必须有其他条款,特别是最新的和最内在的陈述。 在C中,不存在“其他”这样的事情。 只有如果和其他。 相反,根据MISRA的说法,这个构造应该以这种方式正式缩进(我应该把开放的大括号放在他们自己的路线上,但是我不喜欢这样做):
if (A) { // do something } else { if (B) { // do something else (no pun intended) } else { // don't do anything here } }
当MISRA要求在每个分支上放置大括号时,就会提到“if … else if constructs”这个矛盾。
任何人都可以想象如果其他树木的深度嵌套的丑陋, 在这里看到一个侧面说明 。 现在想象一下,这个结构可以任意地扩展到任何地方。 然后最后要求另外一个条款,而不是其他任何地方,都变得荒谬。
if (A) { if (B) { // do something } // you could to something here } else { // or here if (B) { // or C? // do something else (no pun intended) } else { // don't do anything here, if you don't want to } // what if I wanted to do something here? I need brackets for that. }
所以我相信,那些制定MISRA指导方针的人,如果有意图的话,就会有一种类似于if-else的转变。
最后,对于他们来说,精确地定义“if … else if构造”意味着什么,
其基本原因可能是代码覆盖率和隐含的其他因素:如果条件不成立,代码将如何工作? 对于真正的testing,你需要一些方法来看到你已经testing了条件为false。 如果每个testing用例都经过了if子句,那么您的代码可能会在现实世界中遇到问题,因为您没有testing这个条件。
但是,有些条件可能与示例1类似,比如在纳税申报表上:“如果结果小于0,则input0”。 你仍然需要在条件为假的情况下进行testing。
逻辑上任何testing意味着两个分支。 如果这是真的,你会怎么做,如果是假的,你会怎么做?
对于任何一个分支没有function的情况,增加一个关于为什么它不需要function的评论是合理的。
这对于下一位维护程序员来说可能是有益的。 他们不应该search太远,以确定代码是否正确。 你可以种Prehunt大象 。
就个人而言,它帮助我,因为它迫使我看看其他情况,并对其进行评估。 这可能是一个不可能的条件,在这种情况下,我可以违反合同违反。 这可能是良性的,在这种情况下,评论可能就足够了。
你的旅费可能会改变。
大多数时候你只有一个if
语句,这可能是以下原因之一:
- 职能警卫检查
- 初始化选项
- 可选的处理分支
例
void print (char * text) { if (text == null) return; // guard check printf(text); }
但是,如果你这样做, if .. else if
这可能是其中一个原因,如:
- dynamic切换大小写
- 处理叉子
- 处理一个处理参数
如果你的if .. else if
覆盖了所有的可能性,在这种情况下,你最后一个if (...)
是不需要的,你可以删除它,因为在这一点上唯一可能的值是那个条件所覆盖的值。
例
int absolute_value (int n) { if (n == 0) { return 0; } else if (n > 0) { return n; } else /* if (n < 0) */ // redundant check { return (n * (-1)); } }
而在大多数这些原因中,有可能有东西不适合你的if .. else if
任何类别,因此需要在最后的else
子句中处理它们,处理可以通过业务级别的过程来完成,用户通知,内部错误机制,..等等。
例
#DEFINE SQRT_TWO 1.41421356237309504880 #DEFINE SQRT_THREE 1.73205080756887729352 #DEFINE SQRT_FIVE 2.23606797749978969641 double square_root (int n) { if (n > 5) return sqrt((double)n); else if (n == 5) return SQRT_FIVE; else if (n == 4) return 2.0; else if (n == 3) return SQRT_THREE; else if (n == 2) return SQRT_TWO; else if (n == 1) return 1.0; else if (n == 0) return 0.0; else return sqrt(-1); // error handling }
这个最后的else
子句与Java
和C++
等语言中的其他几个非常类似,比如:
-
default
语句在switch语句中 -
catch(...)
所有具体的catch
块 -
finally
是一个try-catch条款
我目前正在使用PHP。 创buildregistry单和login表单。 我只是纯粹使用if和else。 没有别的,如果什么是不必要的。
如果用户点击提交button – >它会转到下一个if语句…如果用户名小于'X'字符数然后警报。 如果成功,然后检查密码长度等。
不需要额外的代码,如其他代码,如果这可能会消除可靠性的服务器加载时间来检查所有额外的代码。
我们的软件不是任务关键,但是我们也决定使用这个规则,因为防守编程。 我们为理论上不可达的代码添加了一个抛出exception(switch + if-else)。 当软件快速失败时,它节省了很多时间,比如当一个新的types被添加了,我们忘记了改变一个或两个if-else或者switch。 作为一个奖金,它超级容易find问题。
那么,我的例子涉及到未定义的行为,但有时候有些人会幻想并失败,请看看:
int a = 0; bool b = true; uint8_t* bPtr = (uint8_t*)&b; *bPtr = 0xCC; if(b == true) { a += 3; } else if(b == false) { a += 5; } else { exit(3); }
你可能永远不会期望有不true
或false
bool
,但是可能会发生。 我个人认为这是由决定做某事的人造成的问题,但另外的陈述可以防止任何进一步的问题。