if 分支语句是C语言程序开发中不可缺少的控制结构之一,它广泛存在于各个C语言程序中。甚至有程序员认为写程序就是将各种情况都考虑到,然后使用if-else语句将这些情况描述出来。
问题
不过,在C语言程序开发中,滥用 if 语句常常会让整个代码变得啰嗦且不可读,C语言初学者小明也发现了这一点。下面是一段C语言代码示例,请看:
bool conditionA = executeStepA();
if (conditionA){
bool conditionB = executeStepB();
if (conditionB){
bool conditionC = executeStepC();
if (conditionC){
...
}
}
}
executeThisFunctionInAnyCase();
executeStepX() 函数只有在前一个函数执行结果为真时才会执行,而 executeThisFunctionInAnyCase() 函数则无论如何都会执行。小明觉得虽然这样写C语言代码可以实现需求,但是 if 语句链显然不是一种优雅的写法,他希望在自己的代码中避免 if 语句链出现,因此尝试对上述C语言代码做了如下修改,请看:
bool conditionA = executeStepA();
if (!conditionA) return;
bool conditionB = executeStepB();
if (!conditionB) return;
bool conditionC = executeStepC();
if (!conditionC) return;
修改后的C语言代码看起来简洁多了,但是却不能满足需求——原先 executeThisFunctionInAnyCase() 函数无论如何都会执行,而现在它只会在 executeStepX() 函数都成功才会执行。
怎样才能避免 if 语句链,并且完美实现小明的需求呢?
按照小明的思路,其实只要保证 executeThisFunctionInAnyCase() 函数无论如何都被执行就可以了,那完全可以多定义一个函数解决这个问题,相关的C语言代码如下,请看:
void foo()
{
bool conditionA = executeStepA();
if (!conditionA) return;
bool conditionB = executeStepB();
if (!conditionB) return;
bool conditionC = executeStepC();
if (!conditionC) return;
}
void bar()
{
foo();
executeThisFunctionInAnyCase();
}
这样就按照小明的思路解决了问题。当然了,问题的答案不止一个,另外一种非常推荐的做法是使用“短路表达式”,相关的C语言代码如下,请看:
关于“短路表达式”的概念,可以参考我之前的文章《》;
if (executeStepA() && executeStepB() && executeStepC()){
...
}
executeThisFunctionInAnyCase();
C语言程序的“短路表达式”决定了只有 executeStepA() 函数成功才会接着执行 executeStepB() 函数,只有 executeStepB() 函数也成功,才会执行 executeStepC() 函数。而无论如何,executeThisFunctionInAnyCase() 函数都会被执行,这样就以较为简洁的方式解决了小明遇到的问题。
另外,还有一种很多C语言程序员都鄙视的方法——使用 goto 语句。我知道很多学校很多教材都非常抵制 goto 语句的“使用”,这里的“使用”打上了引号,是因为我认为他们反对的应该是 goto 语句的滥用,事实上,恰当的使用 goto 语句能够在很多时候简化C语言代码,例如:
int foo() {
int result = /*some error code*/;
if(!executeStepA()) goto cleanup;
if(!executeStepB()) goto cleanup;
if(!executeStepC()) goto cleanup;
result = 0;
cleanup:
executeThisFunctionInAnyCase();
return result;
}
这样的代码风格在 Linux 内核代码中相当常见。也有些程序员习惯使用 do{}while(0) 包裹代码,并且通过 break 关键字提前跳出代码块,但实际上两种方法是一致的。如果还有其他的一些工作需要处理,goto 语句更方便,例如:
int foo() {
int result = /*some error code*/;
if(!executeStepA()) goto cleanupPart;
if(!executeStepB()) goto cleanup;
if(!executeStepC()) goto cleanup;
result = 0;
cleanup:
innerCleanup();
cleanupPart:
executeThisFunctionInAnyCase();
return result;
}
这种情况下如果使用 do{}while(0) 方法,就需要使用不止一个循环体,要是如此类推下去,循环体本身也会带来代码复杂度,这样的技巧就显得有些鸡肋了。
小结
本节通过一个例子,简要的讨论了在C语言代码中避免 if 语句链的几种常用方法。当然了,本节只是抛砖引玉,相信读者肯定也有自己的技巧,欢迎在评论区讨论。同时,我们也能看出,C语言程序开发并不只是使用计算机语言描述“流水账”,多思考,总是能写出更加简洁优雅的代码的。