嵌入式工程師瑞士刀 (2) C語言函數,為什麼0代表成功,非0代表失敗
緣起
我們Team長得比下圖Aerith妹妹還正的女生問,為什麼C函數回傳值是0代表成功?他覺得回傳1(True)比較符合直覺。所以我決定寫下這篇文章,好好解釋理由傳教用
UNIX命令EXIT Code,以及用途
下圖執行ls用echo $?觀察EXIT code。慣例是:正確執行回傳0;否則回傳非0
ls的手冊也有數值說明
具體EXIT code只是main function回傳值,修改Hello World做實驗,沒什麼神秘
按照此慣例,MAKEFILE執行完指令檢查EXIT code,非0會停下。所以MAKEFILE成功跑完代表大家都回傳0沒遇到錯誤,簡單易懂
回到學妹的問題,為什麼C函數用0代表成功
- 最嚴謹的寫法,每個函數都要定義自己的enum標示成功失敗,長得像下面這樣
enum {
SUCCESS = x
FAIL1 = y
FAIL2 = z
}
每個函數的回傳值都要定義對應的enum {},這樣軟體寫起來顯然有點長
假如只有成功或失敗,大家也接受UNIX慣例,這樣能寫得很精簡
return 0; // SUCCESS
return -1; // FAIL - 一般CPU做完運算更新狀態暫存器,內部zero flag代表結果是否為0,所以判斷0是免費的,效能也好。在眾多數值裡也有獨一無二的意義:正確只有一種,但是錯誤卻可能有N種。所以UNIX慣例用0作為成功,也不是隨便亂選
- 在組合語言的層級,判斷0與非零會差一道指令,起碼這麼多年,我是這麼認為
// 0 as SUCCESS, 2 instructions needed
bl some_function
cmp r0
beq some_label // branch if r0 == 0
// 1 as SUCCESS, 3 instructions needed
bl some_function
ldi r1, #1
sub r0, r0, r1 // r0 = r0 - r1, extra instruction
beq some_label // branch if r0 == 0 - 下圖在樹莓派做實驗,比對#1,或是#0,指令數一樣多。原因是ARM的cmp指令能把常數編碼進去,不需要多一道指令。現代CPU一般都有這種指令,所以差距不明顯。假如不考慮指令集特異功能,比對0還是效能略好
正確只能有一種(0),但是錯誤卻可能有多種(non-zero),所以把獨一無二的zero保留給正確,即使效能沒差太多。這個慣例也能用在MAKEFILE串接命令,事情背一種就好,所以我寫軟體也習慣0代表正確
大多數人不會特別堅持這個事,只要把原因說清楚,大家就能接受了。我相信學妹看完這篇文章,為了避免我再囉唆下去(情緒勒索),就會接受正確要回傳0了
Reference
其他在StackOverflow的討論
留言