# Recursive 版本的二進位加法 Recursive Function 問題 大家早安,我在用遞迴實作二進位加法 的時候, 如果我把最後一行的 return 拿掉,並用 gdb 觀察, 發現程式的行為並不會改變,那這個 return 是否有必要加呢? 還是我對 recursive 有什麼誤解? 以下是我的程式碼: 謝謝。 [討論串](https://www.facebook.com/groups/system.software2020/permalink/437196997214505/?__cft__[0]=AZV_gRiCpD80VqBhCy9izPoXBK5LuPYzZhSA0DRcIMLjZMrBvJS1xahlx21l_7lj-zWSQYrvphahzMJa25L_1TyqL1RnMY0wWb_TVc5K7um7G9SlzeD9-1FxIRZ9kkhMV9NsfCt_ow4pcMnz5ggBkVXBo6J_v8TNaU18sEaVX84S7VTtg547KeUBAIgL6JAFJ-s&__tn__=%2CO%2CP-R) ## 完整程式碼 ```cpp=1 #include <stdio.h> #include <stdlib.h> int adder(int a, int b) { int sum = a ^ b; int carry = a & b; if (carry == 0) return sum; adder(sum, carry << 1); } int main(int argc, char **argv) { if (argc != 3) { printf("Usage: ./adder a b\n"); return -1; } int a=atoi(argv[1]); int b=atoi(argv[2]); int result = adder(a, b); printf("%d + %d = %d", a, b, result); } ``` ## 測試方法 使用 gdb 並設置 break point 在 adder 這個 function, 與第十行,也就是 adder這個 function 結束的地方。 測試參數: `a = 3, b = 2` ### 情況一: line 9 沒有加上 return ```cpp=3 int adder(int a, int b) { int sum = a ^ b; int carry = a & b; if (carry == 0) return sum; adder(sum, carry << 1); } ``` ```shell (gdb) b adder Breakpoint 1 at 0x6d8: file adder.c, line 5. (gdb) b 10 Breakpoint 2 at 0x707: file adder.c, line 10. (gdb) r 3 2 Starting program: /home/ubuntu/sandbox/adder/adder 3 2 Breakpoint 1, adder (a=3, b=2) at adder.c:5 5 int sum = a ^ b; (gdb) n 6 int carry = a & b; (gdb) n 7 if (carry == 0) (gdb) n 9 adder(sum, carry << 1); (gdb) n Breakpoint 1, adder (a=1, b=4) at adder.c:5 5 int sum = a ^ b; (gdb) n 6 int carry = a & b; (gdb) n 7 if (carry == 0) (gdb) n 8 return sum; (gdb) n Breakpoint 2, adder (a=1, b=4) at adder.c:10 10 } (gdb) n Breakpoint 2, adder (a=3, b=2) at adder.c:10 10 } (gdb) n main (argc=3, argv=0x7fffffffe4c8) at adder.c:21 21 printf("%d + %d = %d", a, b, result); (gdb) n 22 } (gdb) n __libc_start_main (main=0x555555554709 <main>, argc=3, argv=0x7fffffffe4c8, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffe4b8) at ../csu/libc-start.c:344 344 ../csu/libc-start.c: No such file or directory. (gdb) n 3 + 2 = 5[Inferior 1 (process 31573) exited normally] ``` ### 情況二: line 9 加上 return ```diff --- a/adder/adder.c +++ b/adder/adder.c @@ -6,7 +6,7 @@ int adder(int a, int b) int carry = a & b; if (carry == 0) return sum; - adder(sum, carry << 1); + return adder(sum, carry << 1); } ``` ``` (gdb) b 10 Breakpoint 1 at 0x707: file adder.c, line 10. (gdb) b adder Breakpoint 2 at 0x6d8: file adder.c, line 5. (gdb) r 3 2 Starting program: /home/ubuntu/sandbox/adder/adder 3 2 Breakpoint 2, adder (a=3, b=2) at adder.c:5 5 int sum = a ^ b; (gdb) n 6 int carry = a & b; (gdb) n 7 if (carry == 0) (gdb) n 9 return adder(sum, carry << 1); (gdb) n Breakpoint 2, adder (a=1, b=4) at adder.c:5 5 int sum = a ^ b; (gdb) n 6 int carry = a & b; (gdb) n 7 if (carry == 0) (gdb) n 8 return sum; (gdb) n Breakpoint 1, adder (a=1, b=4) at adder.c:10 10 } (gdb) n Breakpoint 1, adder (a=3, b=2) at adder.c:10 10 } (gdb) n n main (argc=3, argv=0x7fffffffe4c8) at adder.c:21 21 printf("%d + %d = %d", a, b, result); (gdb) n 22 } (gdb) n __libc_start_main (main=0x555555554709 <main>, argc=3, argv=0x7fffffffe4c8, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffe4b8) at ../csu/libc-start.c:344 344 ../csu/libc-start.c: No such file or directory. (gdb) n 3 + 2 = 5[Inferior 1 (process 31628) exited normally] ``` ## 討論區節錄 >注意編譯器的警告訊息: "warning: control may reach end of non-void function [-Wreturn-type]" 在移去 return 關鍵字的狀況下,產生出來的機械碼就取決於編譯器內部實作,而非 C 語言規範 >:notes: Jserv老師 補上資料 [x86-64 clang 10.0.0](https://godbolt.org/z/5vxKPz) (移除 return 關鍵字) ```shell <source>:10:1: warning: non-void function does not return a value in all control paths [-Wreturn-type] } ^ 1 warning generated. ``` >x86 的 return 值會放在 eax,如果沒有 return,對 caller 而言會把 eax 中的數值視為 return 值 >[name=翁愷邑] >因為x86的calling convention會把return value用eax來存。那如果沒有在callee結尾加return,其實gcc compiler還是會把調整stack跳回caller做好,所以這時候caller拿到的return value就是eax,然後剛好你在返回caller的時候,eax都沒動到 >[name=陳廣翰] >補充一下 http://c0x.coding-guidelines.com/6.9.1.html 1844行有說此行為是ub >[name=Yeting Kuo] > >使用 gcc 10.2 可以看到兩者一樣, 但換成 使用 clang 10.0 就變成剛好就差一個 mov eax 所以才會出錯 >[比較 x86-64 clang 10.0.0 編出的 assembly: 有 return v.s. 沒有 return](https://godbolt.org/z/veW156) >[name=翁愷邑] ## TODO: 了解函式呼叫的 assembly code [你所不知道的 C 語言:函式呼叫篇](/@sysprog/c-function?type=view)