【问题标题】:Is the 'else if' redundant in this function?这个函数中的“else if”是多余的吗?
【发布时间】:2016-08-27 16:30:22
【问题描述】:

所以,在 C 程序中用下面的函数来判断一个数是素数还是合数:

/** 
 * Determine if a given integer is prime. O(sqrt n).
 * n: The integer to examine.
 * return: TRUE if n is prime; FALSE if n is not prime.
 */
BOOL APIENTRY IsPrime(int n)
{
    int i;
    double test;
    if(n <= 1) return FALSE;
    else if(n <= 3) return TRUE; // Is the else keyword at beginning of this line useful?

    for(i = 2; i <= sqrt(n); i++)
    {
        test = (double) n / (double) i;
        if (test == floor(test)) return FALSE;
    }
    return TRUE;
}

如果我只用 if (n &lt;= 3) return TRUE; 替换第 12 行,该函数仍然可以正常工作(因为前面的 if 语句在 n 为 1、0 或负数的前提下捕获了 n 不是素数的情况)。无论如何我都应该保留else 关键字吗?

【问题讨论】:

  • 我相信在这种情况下这是一个风格问题。
  • 用浮点运算来做整数计算,最终很可能会出问题。
  • 请记住,源代码有两种受众:编译器和维护者。正如您所指出的,编译器有望识别return 并使else 变得多余。但是维护者呢?如果else 增加了价值——通过与他们沟通这是一个独家案例——然后保留它。

标签: c if-statement return control-flow


【解决方案1】:

在这种情况下,这是无关紧要的,因为您是从代码中返回的。在这两种情况下,编译后的代码都会在n &gt; 1 时跳转到if (n &lt;= 3),无论如何。

这是因为如果第一个 if 语句为假,则必须始终评估第二个条件。这可以在两个函数的汇编输出中很容易地看到(并且应该始终直接验证):

Ltmp7:
    cmpl    $2, %edi
    jge LBB0_2
    xorl    %eax, %eax
    jmp LBB0_8 // jump to return false
LBB0_2:
    movb    $1, %r14b
    cmpl    $4, %edi
    jl  LBB0_3 // jump to return true

Ltmp15:
    cmpl    $2, %edi
    jge LBB1_2
    xorl    %eax, %eax
    jmp LBB1_8 // jump to return false
LBB1_2:
    movb    $1, %r14b
    cmpl    $4, %edi
    jl  LBB1_3 // jump to return true

通过将代码修改为类似的方式给出了一个有趣的转折

if (n <= 3) return n > 1;

因为现在你只有一个分支,所以它可以进行不同的优化,例如在我的编译器 (clang-3.4) 上它产生

cmpl    $4, %edi
jge LBB0_1 // jump to function body
cmpl    $1, %edi
setg    %al
jmp LBB0_7 // jump to return with $al register already set

它使用setg指令直接根据前面的比较设置返回值。

【讨论】:

    【解决方案2】:
    if(n <= 1) return FALSE;
    else if(n <= 3) return TRUE;
    

    if(n <= 1) return FALSE;
    if(n <= 3) return TRUE;
    

    是相同的,因为return FALSE; 结束当前函数的执行(与任何return 语句一样)。所以生成的汇编代码应该是一样的,你可以保留也可以不保留。

    【讨论】:

    • return 结束执行,无论TRUE 还是FALSE
    【解决方案3】:

    是的,if 分支无条件返回之后的 else 总是多余的:

    if (someCondition) {
        ... do something, then
        return someValue;
    } // <<== An "else" here would be redundant
    ... more code
    

    到达“更多代码”行的唯一方法是让someCondition 计算为false,这与到达else 分支的条件相同。

    if 分支与其他类型的无条件控制转移也是如此,例如breakcontinue。存在代码分析工具来警告程序员冗余。

    【讨论】:

      【解决方案4】:

      如果您的编译器足够聪明,编译后的代码将与else 之前的else 相同。

      但是,在您的代码中,您写道:

      for (...; n<= sqrt(n); ...)
      

      在每次循环迭代中哪个可以(这又取决于编译器)调用sqrt 函数。

      替换为:

      int limit = sqrt(n);
      for (i = 3; i <= limit; i +=2)
          ...
      

      【讨论】:

        【解决方案5】:

        这取决于你,真的

        而且,i 是从 2 开始的,所以是的,你可以丢弃它。


        题外话:为什么要使用浮点运算?你想找到素数。你可能会遇到问题,小心!

        【讨论】:

        • 我正在使用浮点运算,因为我想不出比进行除法和测试结果的 floor() 更好的方法来测试找到 n 的因子。
        • @GovindParmar,从我的代码中获取灵感:gsamaras.wordpress.com/code/isprime-c
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-07-21
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多