我们的组织有一个必需的编码规则(没有任何解释):
if ... 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 */
}
这个设计要处理的边缘情况是什么?
我还担心的原因是示例1 不需要else
但示例2 。如果原因是可重用性和可扩展性,我认为else
应该在两种情况下都使用。
答案 0 :(得分:144)
如另一个答案所述,这是来自MISRA-C编码指南。其目的是防御性编程,这是一种常用于任务关键型编程的概念。
也就是说,每个if - else if
必须以else
结尾,而每个switch
必须以default
结尾。
这有两个原因:
自我记录代码。如果你写一个else
但是把它留空则意味着:&#34;当if
和else if
都不为真时,我已经明确地考虑了这个场景&#34;。
不写else
就意味着:&#34;要么我考虑了if
和else if
都不是真的情况,要么我完全忘了考虑那个&#39} ;在我的代码中,这可能是一个肥胖的错误&#34;。
停止失控代码。在任务关键型软件中,您需要编写可靠的程序,即使是极不可能的程序。所以你可以看到像
这样的代码if (mybool == TRUE)
{
}
else if (mybool == FALSE)
{
}
else
{
// handle error
}
这段代码与PC程序员和计算机科学家完全不同,但它在任务关键型软件中非常有意义,因为它可以捕获&#34; mybool&#34;无论出于什么原因,它已经腐败了。
从历史上看,由于EMI /噪声,您会担心RAM内存损坏。今天这不是什么大问题。更有可能的是,由于代码中其他地方的错误导致内存损坏:指向错误位置的指针,数组越界错误,堆栈溢出,失控代码等。
所以大多数时候,当你在实现阶段编写错误时,像这样的代码回过头来打击自己。这意味着它也可以用作调试技术:您编写的程序会在您编写错误时告诉您。
修改强>
关于为什么在每个else
之后不需要if
:
if-else
或if-else if-else
完全涵盖变量可以拥有的所有可能值。但是,一个简单的if
声明并不一定涵盖所有可能的值,它具有更广泛的用法。大多数情况下,您只是希望检查某个条件,如果不满足,则不执行任何操作。那么编写防御性编程以涵盖else
案例就没有意义了。
另外,如果你在每个else
之后写了一个空的if
,它会完全混乱代码。
MISRA-C:2012 15.7没有理由说明为什么不需要else
,它只是声明:
注意:简单
else
不需要最终的if
语句 言。
答案 1 :(得分:61)
贵公司遵循MISRA编码指南。这些指南的几个版本包含此规则,但来自MISRA-C:2004 †:
规则14.10(必填):所有如果......如果结构将被终止 用else子句。
只要if语句后跟一个或多个语句,此规则就适用 否则如果陈述;最后的其他
if
后跟else
声明。如果是简单if
语句,那么else
声明不需要包括在内。最终else
的要求 声明是防御性编程。else
声明应该是 采取适当行动或包含适当的评论,为什么不 采取行动。这符合要求default
语句中的最终switch
子句。例如这段代码 是一个简单的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版本,是当前对新项目的推荐,the same rule exists but is numbered 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 webstore(via)购买这些出版物的当前版本和过去版本。
答案 2 :(得分:19)
这相当于在每个开关中都要求默认情况。
这个额外的其他内容会降低您计划的code coverage。
根据我将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.
*/
}
答案 3 :(得分:9)
只是一个简短的解释,因为我大约在5年前做过这个。
(大多数语言)没有语法要求包括&#34; null&#34; else
声明(和不必要的{..}
),以及简单的小程序&#34;没有必要。但是,真正的程序员不会编写简单的小程序,而且,同样重要的是,他们不会编写将被使用过一次然后丢弃的程序。
当写一个if / else:
时if(something)
doSomething;
else
doSomethingElse;
这一切看起来都很简单,即使添加{..}
也很难看到。
但是有一天,从现在起的几个月,其他一些程序员(你永远不会犯这样的错误!)将需要&#34;增强&#34;该计划并将添加一份声明。
if(something)
doSomething;
else
doSomethingIForgot;
doSomethingElse;
突然doSomethingElse
忘记了它应该在else
腿。
所以你是一个优秀的小程序员,你总是使用{..}
。但你写道:
if(something) {
if(anotherThing) {
doSomething;
}
}
一切顺利,直到这个新孩子进行午夜修改:
if(something) {
if(!notMyThing) {
if(anotherThing) {
doSomething;
}
else {
dontDoAnything; // Because it's not my thing.
}}
}
是的,格式不正确,但项目代码的一半,以及&#34;自动格式化程序&#34;被所有#ifdef
语句搞砸了。当然,真正的代码比这个玩具示例复杂得多。
不幸的是(或者不是),我已经退出了这种事情几年了,所以我没有一个新的&#34;真实的&#34;记住一个例子 - 上面(显然)做作并且有点蠢。
答案 4 :(得分:7)
这样做是为了使代码更具可读性,以便以后的引用,并向后来的审阅者明确说明,上一个else
处理的其余案例什么都不做强烈的>案件,这样他们一见钟情就不会被忽视。
这是一个很好的编程习惯,它使代码可重用和可扩展。
答案 5 :(得分:6)
我想补充一下 - 并且部分地反驳 - 以前的答案。虽然使用if-else肯定是常见的,如果以类似开关的方式覆盖表达式的全部可思考值,则无法保证完全覆盖任何可能的条件范围。关于switch构造本身也是如此,因此要求使用default子句,它捕获所有剩余的值,并且如果不是其他方面的话,可以用作断言保护。
问题本身具有一个很好的反例:第二个条件与x完全无关(这就是为什么我经常更喜欢基于交换机的变体更灵活的基于if的变体)。从示例中可以明显看出,如果满足条件A,则应将x设置为某个值。如果不满足A,则测试条件B.如果满足,则x应该接收另一个值。如果既不满足A也不满足B,则x应保持不变。
在这里我们可以看到应该使用一个空的else分支来评论程序员对读者的意图。
另一方面,我不明白为什么必须有一个else子句,尤其是最新的和最里面的if语句。 在C中,没有&#39;否则如果&#39;。只有if和else。相反,根据MISRA,构造应该正式缩进(我应该把开口的花括号放在他们自己的行上,但我不喜欢这样) :
if (A) {
// do something
}
else {
if (B) {
// do something else (no pun intended)
}
else {
// don't do anything here
}
}
当MISRA要求在每个分支周围放置花括号时,通过提及&#34; if ... else if construct&#34;。
任何人都可以想象深度嵌套if else树的丑陋,see here on a side note。现在想象一下这个结构可以在任何地方任意扩展。然后最后要求一个else子句,而不是其他任何地方,变得荒谬。
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。
最后,他们准确地定义了&#34; if ... else if的含义&#34;
答案 6 :(得分:5)
逻辑上,任何测试都意味着两个分支。如果是真的你会怎么做?如果它是假的你怎么办?
对于任何一个分支都没有功能的情况,添加一个关于它不需要具有功能的原因的评论是合理的。
这对下一位维护程序员来说可能是有益的。他们不应该搜索太多来决定代码是否正确。你可以Prehunt the Elephant。
就个人而言,它帮助我,因为它迫使我看看其他案例,并对其进行评估。这可能是一个不可能的条件,在这种情况下,我可能会因违反合同而抛出异常。这可能是良性的,在这种情况下评论可能就足够了。
您的里程可能会有所不同。
答案 7 :(得分:5)
基本原因可能是代码覆盖率和隐含的其他原因:如果条件不正确,代码将如何表现?对于真正的测试,您需要一些方法来确定您已经使用条件false进行了测试。如果您拥有的每个测试用例都经过了if子句,那么您的代码可能会在现实世界中出现问题,因为您没有测试它。
但是,某些条件可能与示例1类似,例如在纳税申报单上:“如果结果小于0,则输入0”。您仍然需要在条件为假的情况下进行测试。
答案 8 :(得分:4)
大多数情况下,当您只有一个if
声明时,可能是以下原因之一:
实施例
void print (char * text)
{
if (text == null) return; // guard check
printf(text);
}
但是当你执行if .. else if
时,可能是以下原因之一:
如果您的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
try-catch子句中的finally
答案 9 :(得分:2)
Our software was not mission critical, yet we also decided to use this rule because of defensive programming. We added a throw exception to the theoretically unreachable code (switch + if-else). And it saved us many times as the software failed fast e.g. when a new type has been added and we forgot to change one-or-two if-else or switch. As a bonus it made super easy to find the issue.
答案 10 :(得分:2)
嗯,我的例子涉及未定义的行为,但有时候有些人试图变得花哨并且努力失败,看看:
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);
}
你可能永远不会期望bool
不是true
也不是false
,但它可能会发生。就个人而言,我认为这是由决定做某事的人造成的问题,但额外的else
声明可以防止任何进一步的问题。
答案 11 :(得分:1)
我目前正在使用PHP。创建注册表单和登录表单。我只是纯粹使用if和else。没有其他不必要的东西。
如果用户点击提交按钮 - &gt;它转到下一个if语句...如果用户名小于&#39; X&#39;然后提醒人物的数量。如果成功,则检查密码长度等。
如果可以解除服务器加载时间的可靠性以检查所有额外的代码,则不需要额外的代码,例如else。