進不去的 if-else 條件運算式 – C 語言鬼故事

在工作上曾經遇到關於 if-else 的 bug ,明明 if 後的 expression 為 true ,但卻無法執行對應的程式碼。

進不去的程式碼

用以下程式為例, foo() 會根據 a 和 b 的值而有不同的回傳值。

#include <stdio.h>
#include <stdlib.h>

int foo (int a, int b)
{
    if (a==1)
        if (b==1)
            return 1;
    else if (a==0)
        if (b==1)
            return 2;
    else
        return -1;
    return 0;
}

int main (int argc, char *argv[])
{
    int ans = foo (atoi(argv[1]), atoi(argv[2]));
    printf ("%d", ans);
    return 0;
}

執行程式並觀察他的輸出:

~/workspace/others 
❯ ./a.out 1 1
1
~/workspace/others 
❯ ./a.out 0 1
0

當輸入 a = 1, b = 1 時,輸出為 1 ,這個跟預期一樣。

當 a = 0, b = 1 的時候,得到的輸出卻是 0 ,為什麼輸出不是 2 呢???

碰到這個情況,大家第一直覺,一定是將每個 if 和 else 後的 statement 用括號包好,確實如果將括號正確包好,當輸入 a=0, b=1 時,輸出確實變為 2 。但為什麼前面狀況下得到輸出卻是 0 ...

大文豪寫作需要查字典,饒舌歌手為了押韻也需要查字典,宅宅工程師寫程式碼難道不用查規格書嗎!?

規格書怎麼說

c99 規格書中, 6.4.8.1 The if statement 章節,針對 else 就有以下描述:

"An else is associated with the lexically nearest preceding if that is allowed by the syntax."

也就是說 else 會往前找到第一個 if 來跟他連結。

讓我再將上面的例子修改一下,幫助思考上面規格書的定義。

int foo (int a, int b, int c)
{
    if (a==1)
        if (b==1)
            return 1;
    else if (b==2)
        if (c==1)
            return 2;
    else
        return -1;
    return 0;
}

執行結果如下,你可以試著看看能不能排版好程式碼,正確排版會在最下面。

❯ ./a.out 0 1 1
0
~/workspace/others 
❯ ./a.out 1 1 1
1
~/workspace/others 
❯ ./a.out 1 2 1
2
~/workspace/others 
❯ ./a.out 1 2 0
-1
~/workspace/others 
❯ ./a.out 1 3 1
0
int foo (int a, int b)
{
    if (a==1) {
        if (b==1)
            return 1;
        else if (b==2) {
             if (c==1)
                return 2;
             else
                return -1;
        }
    }
    return 0;
}

從以上例子,學到兩個教訓:

  1. if-else 後面的 statement 請用括號正確包好
  2. else 會往前找到對應的 if 配對,絕對不要以為你自己做好排版,if-else 就會按照你所想的執行。