2013年1月14日 星期一

改變指標的內容需要傳指標的指標

L22 mp(p, 5) 應該是很多人的困擾, 為什麼 p 這個指標不會被改成 5 還是原來的 1。

ptrptr.c
 1 /*
 2  *  modify pointer
 3  */
 4 #include <stdio.h>
 5 
 6 void mp(char *ptr, int addr)
 7 {
 8   ptr = addr;
 9   printf("ptr: %p\n", ptr);
10 }
11 


12 void mp1(char **ptrptr, int addr)
13 {
14   *ptrptr = addr;
15   printf("*ptrptr: %p\n", *ptrptr);
16 }
17 
18 int main(int argc, const char *argv[])
19 {
20   char *p=1;
21   printf("p: %p\n", p);
22   mp(p, 5);       
23   printf("p: %p\n", p);
24   mp1(&p, 5);       
25   printf("p: %p\n", p);
26   return 0;
27 }

執行結果:
p: 0x1
ptr: 0x5
p: 0x1
*ptrptr: 0x5
p: 0x5

組合語言面前沒有秘密, 硬著頭皮看看吧!

objdump -d ptrptr
 1 08048404 <mp>:
 2  8048404: 55                    push   %ebp
 3  8048405: 89 e5                 mov    %esp,%ebp
 4  8048407: 83 ec 18              sub    $0x18,%esp
 5  804840a: 8b 45 0c              mov    0xc(%ebp),%eax
 6  804840d: 89 45 08              mov    %eax,0x8(%ebp)
 7  8048410: b8 90 85 04 08        mov    $0x8048590,%eax
 8  8048415: 8b 55 08              mov    0x8(%ebp),%edx
 9  8048418: 89 54 24 04           mov    %edx,0x4(%esp)
10  804841c: 89 04 24              mov    %eax,(%esp)
11  804841f: e8 fc fe ff ff        call   8048320 <printf@plt>
12  8048424: c9                    leave  
13  8048425: c3                    ret    
14 
15 08048426 <mp1>:
16  8048426: 55                    push   %ebp
17  8048427: 89 e5                 mov    %esp,%ebp
18  8048429: 83 ec 18              sub    $0x18,%esp
19  804842c: 8b 55 0c              mov    0xc(%ebp),%edx
20  804842f: 8b 45 08              mov    0x8(%ebp),%eax
21  8048432: 89 10                 mov    %edx,(%eax)
22  8048434: 8b 45 08              mov    0x8(%ebp),%eax
23  8048437: 8b 10                 mov    (%eax),%edx
24  8048439: b8 99 85 04 08        mov    $0x8048599,%eax
25  804843e: 89 54 24 04           mov    %edx,0x4(%esp)
26  8048442: 89 04 24              mov    %eax,(%esp)
27  8048445: e8 d6 fe ff ff        call   8048320 <printf@plt>
28  804844a: c9                    leave  
29  804844b: c3                    ret    
30 
31 0804844c <main>:
32  804844c: 55                    push   %ebp
33  804844d: 89 e5                 mov    %esp,%ebp
34  804844f: 83 e4 f0              and    $0xfffffff0,%esp
35  8048452: 83 ec 20              sub    $0x20,%esp
36  8048455: c7 44 24 1c 01 00 00  movl   $0x1,0x1c(%esp)
37  804845c: 00 
38  804845d: 8b 54 24 1c           mov    0x1c(%esp),%edx
39  8048461: b8 a6 85 04 08        mov    $0x80485a6,%eax
40  8048466: 89 54 24 04           mov    %edx,0x4(%esp)
41  804846a: 89 04 24              mov    %eax,(%esp)
42  804846d: e8 ae fe ff ff        call   8048320 <printf@plt>
43  8048472: 8b 44 24 1c           mov    0x1c(%esp),%eax
44  8048476: c7 44 24 04 05 00 00  movl   $0x5,0x4(%esp)
45  804847d: 00 
46  804847e: 89 04 24              mov    %eax,(%esp)
47  8048481: e8 7e ff ff ff        call   8048404 <mp>
48  8048486: 8b 54 24 1c           mov    0x1c(%esp),%edx
49  804848a: b8 a6 85 04 08        mov    $0x80485a6,%eax
50  804848f: 89 54 24 04           mov    %edx,0x4(%esp)
51  8048493: 89 04 24              mov    %eax,(%esp)
52  8048496: e8 85 fe ff ff        call   8048320 <printf@plt>
53  804849b: c7 44 24 04 05 00 00  movl   $0x5,0x4(%esp)
54  80484a2: 00 
55  80484a3: 8d 44 24 1c           lea    0x1c(%esp),%eax
56  80484a7: 89 04 24              mov    %eax,(%esp)
57  80484aa: e8 77 ff ff ff        call   8048426 <mp1>
58  80484af: 8b 54 24 1c           mov    0x1c(%esp),%edx
59  80484b3: b8 a6 85 04 08        mov    $0x80485a6,%eax
60  80484b8: 89 54 24 04           mov    %edx,0x4(%esp)
61  80484bc: 89 04 24              mov    %eax,(%esp)
62  80484bf: e8 5c fe ff ff        call   8048320 <printf@plt>
63  80484c4: b8 00 00 00 00        mov    $0x0,%eax
64  80484c9: c9                    leave  
65  80484ca: c3                    ret    

L43 0x1c(%esp) 就是 c code 的 char *p=1; L46 會把 *p 也就是 1 push 到 stack。


位址 位址的內容
0x100 p 1




x01000 5
0x1004 1 -> 5
0x1008 return address

call mp() 之後, mp() 修改的是位址 0x1004, 改成 5。對於 main() 的 p 完全沒影響。


L 55, 56 push p address (0x100) 到 stack。

位址 位址的內容
0x100 p 1 -> 5


x01000 5
0x1004 0x100
0x1008 return address

call mp1() 之後, mp1() 修改的是位址 0x100, 改成 5。這就改變了 main() 的 p。

這就是 C 只有 call by value 的說法。

有點難懂的話, 我有個口訣:
「傳入函式要改變變數要傳變數的指標; 要改變指標就要傳指標的指標。」

沒有留言:

張貼留言

使用 google 的 reCAPTCHA 驗證碼, 總算可以輕鬆留言了。

我實在受不了 spam 了, 又不想讓大家的眼睛花掉, 只好放棄匿名留言。這是沒辦法中的辦法了。留言的朋友需要有 google 帳號。