---
# System prepended metadata

title: Recursive 版本的二進位加法

---

# 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)