在工作上曾經遇到關於 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;
}
從以上例子,學到兩個教訓:
- if-else 後面的 statement 請用括號正確包好
- else 會往前找到對應的 if 配對,絕對不要以為你自己做好排版,if-else 就會按照你所想的執行。