You are not allowed to perform this action
c:functionreturnvalue
國立屏東大學 資訊工程學系 C語言程式設計
關於函式的傳回值
某次考試有位同學寫了以下的maxBall.c程式碼:
#include <stdio.h>
struct ball
{
int value;
char label[10];
};
typedef struct ball Ball;
void showABall(Ball b)
{
printf("%s(value=%d)\n", b.label, b.value);
}
Ball *maxBall(Ball *a, Ball *b)
{
if(a->value>b->value)
{
a->value=200;
// 正確答案應寫做return a;
}
else
{
b->value=b->value;
// 正確答案應寫做return b;
}
}
int main()
{
Ball b1 ={ 200, "ball 1"};
Ball b2 ={ 0, "ball 2" };
Ball *max;
scanf(" %d", &b2.value);
max=maxBall(&b1, &b2);
printf("max's value=%p\n", max);
showABall(*max);
}
此程式會讓指標max指向b1與b2其value數值較大者,但這位同學在第20及第25行將原本應寫為「return a;」與「return b;」的程式碼,寫錯為「a→value=200;」與「b→value=b→value;」。有趣的是、神奇的是、不可思議的是…. 這個「錯誤」的程式的執行結果竟然是「正確」的!!請參考以下的執行結果:
[23:06 junwu@ws ~]$ cc test.c [23:06 junwu@ws ~]$ ./a.out 20 ball 1(value=200) [23:06 junwu@ws ~]$ ./a.out 300 ball 2(value=300) [23:06 junwu@ws ~]$
其實這位同學所犯的錯誤是在maxBall()函式裡,少寫了return敘述,但卻「幸運地」寫出了「仍然會產生正確結果的不正確」程式。讓我們將上述程式中的maxBall()函式轉譯為組合語言,就可以看出原因了1) – 由於在x86_64架構下,當函式的呼叫完成時會將rax暫存器的值傳回2)(如果該函式有傳回值的話):
maxBall:
push rbp
mov rbp, rsp
mov QWORD PTR [rbp-8], rdi // 將第一個參數值(指標a的值),放入stack
mov QWORD PTR [rbp-16], rsi // 將第二個參數值(指標b的值),也放入stack
mov rax, QWORD PTR [rbp-8] // 把第一個參數值(指標a的值),放入rax暫存器
mov edx, DWORD PTR [rax] // 把rax所指向的地方的值(Ball結構體中的value)放入edx暫存器
// 也就是a->value
mov rax, QWORD PTR [rbp-16] // 把第二個參數值(指標b的值),放入rax暫存器
mov eax, DWORD PTR [rax] // 把rax所指向的地方的值(Ball結構體中的value)放入eax暫存器
// 也就是b->value
cmp edx, eax // 比較a->value與b->value
jle .L2 // if (a->value <=b->value> goto L2
mov rax, QWORD PTR [rbp-8] // ==> else 將第一個參數值(指標a的值),放入rax暫存器
mov DWORD PTR [rax], 200 將rax所指向的地方裡的值設定為200
jmp .L4 // 強制跳躍到L4
.L2:
mov rax, QWORD PTR [rbp-16] // 將第二個參數值(指標b的值),放入rax暫存器
mov edx, DWORD PTR [rax] // 將rax所指向的地方裡的值放入到edx暫存器
mov rax, QWORD PTR [rbp-16] // 將第二個參數值(指標b的值),放入rax暫存器
mov DWORD PTR [rax], edx // 將edx暫存器的值放入到rax暫存器所指向的地方暫存器
.L4:
pop rbp
ret
1)
你可以使用https://godbolt.org/來進行轉譯。
c/functionreturnvalue.txt · 上一次變更: 2022/04/25 15:58 由 junwu
