顯然,以下代碼適用于 GCC。我在onlinegdb.
# include <stdio.h>
int calc_gcd (int a, int b) {
int r = a % b;
if (r == 0) {
return b;
} else {
calc_gcd (b, r);
}
}
int main() {
int a, b, gcd, dividend, divisor;
printf ("Enter two numbers: ");
scanf ("%d%d", &a, &b);
dividend = (a > b) ? a : b;
divisor = (a < b) ? a : b;
gcd = calc_gcd (dividend, divisor);
printf ("GCD = %d\n", gcd);
return 0;
}
但它失敗了clang 13,結果如下
tail_recursion_gcd.c:15:1: warning: non-void function does not return a value in all control paths [-Wreturn-type]
}
^
1 warning generated.
Enter two numbers: 15 10
GCD = 127 // garbage
我不明白。顯然 GCC 所允許的并不直觀,您必須從函式中回傳。
我嘗試了以下方法,但這在 gcc 中不起作用
# include <stdio.h>
int useless_func()
{
3050;
}
int main() {
printf("result = %d", useless_func());
return 0;
}
輸出是result = 0
uj5u.com熱心網友回復:
TL;DR:你的老師給你的 GCD 功能是不可移植的。
你想要的如下:
int calc_gcd (int a, int b) {
int r = a % b;
if (r == 0) {
return b;
} else {
return calc_gcd (b, r);
}
}
您使用的是什么版本的 GCC,在什么作業系統上?
GCC 比 Clang 寬松得多。看起來 GCC 決定該函式calc_gcd不應該失敗并且應該回傳calc_gcd(b, r).
當您使用 Clang 編譯時,我很驚訝它沒有回傳1。當我在 M1 MacBook Pro 上編譯時,它確實如此。
從這里:
從函式的末尾流出相當于沒有值的回傳;這會導致值回傳函式中的未定義行為。
在 x86_64 Linux 上,當呼叫回傳非 void 值的函式時,會在堆疊上為回傳值分配空間。因此,在沒有明確的 return 陳述句的情況下,仍然會回傳一些值。
注意:在 x86_64 上,回傳值存盤在 RAX 中(EAX 是低位)。
我在 x86_64 Ubuntu Server 18.04 上使用 GCC 7.5.0 編譯。我使用的命令是gcc -o gcd gcd.c -no-pie -g. 這是程式集轉儲calc_gcd:
0x00000000004005c7 < 0>: push rbp
0x00000000004005c8 < 1>: mov rbp,rsp
0x00000000004005cb < 4>: sub rsp,0x20
0x00000000004005cf < 8>: mov DWORD PTR [rbp-0x14],edi
0x00000000004005d2 < 11>: mov DWORD PTR [rbp-0x18],esi
0x00000000004005d5 < 14>: mov eax,DWORD PTR [rbp-0x14]
0x00000000004005d8 < 17>: cdq
0x00000000004005d9 < 18>: idiv DWORD PTR [rbp-0x18]
0x00000000004005dc < 21>: mov DWORD PTR [rbp-0x4],edx
0x00000000004005df < 24>: cmp DWORD PTR [rbp-0x4],0x0
0x00000000004005e3 < 28>: jne 0x4005ea <calc_gcd 35>
0x00000000004005e5 < 30>: mov eax,DWORD PTR [rbp-0x18]
0x00000000004005e8 < 33>: jmp 0x4005f9 <calc_gcd 50>
0x00000000004005ea < 35>: mov edx,DWORD PTR [rbp-0x4]
0x00000000004005ed < 38>: mov eax,DWORD PTR [rbp-0x18]
0x00000000004005f0 < 41>: mov esi,edx
0x00000000004005f2 < 43>: mov edi,eax
0x00000000004005f4 < 45>: call 0x4005c7 <calc_gcd>
0x00000000004005f9 < 50>: leave
0x00000000004005fa < 51>: ret
使用 Clang 6.0.0:
0x0000000000400540 < 0>: push rbp
0x0000000000400541 < 1>: mov rbp,rsp
0x0000000000400544 < 4>: sub rsp,0x20
0x0000000000400548 < 8>: mov DWORD PTR [rbp-0x8],edi
0x000000000040054b < 11>: mov DWORD PTR [rbp-0xc],esi
0x000000000040054e < 14>: mov eax,DWORD PTR [rbp-0x8]
0x0000000000400551 < 17>: cdq
0x0000000000400552 < 18>: idiv DWORD PTR [rbp-0xc]
0x0000000000400555 < 21>: mov DWORD PTR [rbp-0x10],edx
0x0000000000400558 < 24>: cmp DWORD PTR [rbp-0x10],0x0
0x000000000040055c < 28>: jne 0x40056d <calc_gcd 45>
0x0000000000400562 < 34>: mov eax,DWORD PTR [rbp-0xc]
0x0000000000400565 < 37>: mov DWORD PTR [rbp-0x4],eax
0x0000000000400568 < 40>: jmp 0x40057b <calc_gcd 59>
0x000000000040056d < 45>: mov edi,DWORD PTR [rbp-0xc]
0x0000000000400570 < 48>: mov esi,DWORD PTR [rbp-0x10]
0x0000000000400573 < 51>: call 0x400540 <calc_gcd>
0x0000000000400578 < 56>: mov DWORD PTR [rbp-0x14],eax
0x000000000040057b < 59>: mov eax,DWORD PTR [rbp-0x4]
0x000000000040057e < 62>: add rsp,0x20
0x0000000000400582 < 66>: pop rbp
0x0000000000400583 < 67>: ret
您可以看到編譯器在優化方面做出了不同的選擇。
事實上,用 GCC,rbp-0x18等于0x00000005乘了 38。有了 Clang,rbp-0x4就等于完全不同了。在我的機器上,在 pwndbg 中,它是0x00007fff.
uj5u.com熱心網友回復:
你的代碼有問題。int只有當值永遠不會被消耗時,才允許從函式回傳而不回傳值。這是 K&R C 的保留,不應再使用。
int calc_gcd (int a, int b) {
int r = a % b;
if (r == 0) {
return b;
} else {
calc_gcd (b, r);
}
}
顯然不正確。你要。
int calc_gcd (int a, int b) {
int r = a % b;
if (r == 0) {
return b;
} else {
return calc_gcd (b, r);
}
}
事實上,這個特定的代碼往往會起作用,-O0因為暫存器中的回傳值是你想要的回傳值。
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/464182.html
上一篇:如何在c中宣告2個結構相互包含
下一篇:將專案添加到堆疊使其崩潰
