亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

Chinaunix

標(biāo)題: c語言函數(shù)形參的地址關(guān)系 [打印本頁]

作者: cao627    時間: 2015-01-03 10:34
標(biāo)題: c語言函數(shù)形參的地址關(guān)系
本帖最后由 cao627 于 2015-01-03 10:40 編輯
  1. include <stdio.h>

  2. void test1(int a,int b, int c)
  3. {
  4.         int * p;
  5.         p = &a;
  6.         printf("%d\n", *p);
  7.         p--;
  8.         printf("%d\n", *p);
  9.         p--;
  10.         printf("%d\n", *p);
  11. }

  12. void test2(const char * s, ...)
  13. {
  14.         int * p;
  15.         p = (int *)&s;
  16.         p++;
  17.         printf("%d\n", *p);
  18.         p++;
  19.         printf("%d\n", *p);
  20. }

  21. int main()
  22. {

  23.         test1(100,200,300);
  24.         test2("hello world",100,200);
  25.         return 0;
  26. }

復(fù)制代碼
看到一個視頻教程,里面用test2實現(xiàn)了:打印第二個參數(shù)和第三個參數(shù)的值,依據(jù)是形參的地址在內(nèi)存里緊挨著的,但在我的環(huán)境里無法實現(xiàn)這一點。

問題:
1.在test2中定義的是指向正整型指針p,對p++,p的步進(jìn)是4,而傳給test2的實參"hello world" 長度大于4個字節(jié),p++后指向的應(yīng)該還是在"hello.world"  這串字符存儲的空間內(nèi),怎么能指到參數(shù)100的存儲空間,視頻教程里確實做到了。

2.在我的環(huán)境里test1的參數(shù)的地址關(guān)系是從左往右遞減的,即如上代碼是用p--實現(xiàn)依次打印第一,第二,第三個參數(shù)。而在視頻教程中是用p++。這跟編譯器不同的關(guān)系嗎?


作者: Dannysd    時間: 2015-01-03 17:48
  1. #include<stdio.h>

  2. void test1(int a,int b, int c)
  3. {
  4.         int * p;
  5.         p = &a;
  6.         printf("%d\n", *p);
  7.         p++;
  8.         printf("%d\n", *p);
  9.         p++;
  10.         printf("%d\n", *p);
  11. }

  12. void test2(const char * s, ...)
  13. {
  14.         int * p;
  15.         p = (int *)&s;
  16.         p++;
  17.         printf("%d\n", *p);
  18.         p++;
  19.         printf("%d\n", *p);
  20. }

  21. int main()
  22. {
  23.         test1(100,200,300);

  24.         test2("hello world",100,200);

  25.         return 0;
  26. }
復(fù)制代碼
  1. ./a.out
  2. 100
  3. 200
  4. 300
  5. 100
  6. 200
復(fù)制代碼

作者: Dannysd    時間: 2015-01-03 17:51
test2反匯編
  1. 080483f9 <test2>:
  2. 80483f9:       55                      push   %ebp
  3. 80483fa:       89 e5                   mov    %esp,%ebp
  4. 80483fc:       83 ec 18                sub    $0x18,%esp
  5. 80483ff:       8d 45 08                lea    0x8(%ebp),%eax
  6. 8048402:       89 45 fc                mov    %eax,0xfffffffc(%ebp)
  7. 8048405:       83 45 fc 04             addl   $0x4,0xfffffffc(%ebp)
  8. 8048409:       8b 45 fc                mov    0xfffffffc(%ebp),%eax
  9. 804840c:       8b 00                   mov    (%eax),%eax
  10. 804840e:       89 44 24 04             mov    %eax,0x4(%esp)
  11. 8048412:       c7 04 24 60 85 04 08    movl   $0x8048560,(%esp)
  12. 8048419:       e8 9a fe ff ff          call   80482b8 <printf@plt>
  13. 804841e:       83 45 fc 04             addl   $0x4,0xfffffffc(%ebp)
  14. 8048422:       8b 45 fc                mov    0xfffffffc(%ebp),%eax
  15. 8048425:       8b 00                   mov    (%eax),%eax
  16. 8048427:       89 44 24 04             mov    %eax,0x4(%esp)
  17. 804842b:       c7 04 24 60 85 04 08    movl   $0x8048560,(%esp)
  18. 8048432:       e8 81 fe ff ff          call   80482b8 <printf@plt>
  19. 8048437:       c9                      leave  
  20. 8048438:       c3                      ret   
復(fù)制代碼
  1. 8048405:       83 45 fc 04             addl   $0x4,0xfffffffc(%ebp)
復(fù)制代碼
這句對應(yīng)的是p++,其實是在棧上向移動到了下一個地址
作者: cao627    時間: 2015-01-03 19:45
對于test1這樣的形式,在我的環(huán)境中要用指針遞減的方式才能依次打印各個參數(shù)。
對于test2這樣的形式,在我的環(huán)境中無論用指針遞增還是遞減都不能打印字符串長量后的各個參數(shù)。

我的環(huán)境是
ubuntu 14.04  64位
gcc 4.8.2
作者: cao627    時間: 2015-01-03 19:53
本帖最后由 cao627 于 2015-01-03 19:54 編輯

@Dannysd
請你在test2中打印一下p取到字符串常量首地址的地址,然后再打印一下p++后的地址?纯磧傻刂返牟睢
作者: hellioncu    時間: 2015-01-03 20:22
研究這個意義不大,不具有可移植性
作者: Dannysd    時間: 2015-01-03 20:57
本帖最后由 Dannysd 于 2015-01-03 20:58 編輯

我是32位環(huán)境,gcc version 4.1.2 Centos
可變參數(shù)這個沒弄懂。。。。
test1
  1. Breakpoint 1, test1 (a=100, b=200, c=300) at c.c:13
  2. 13              p = &a;
  3. (gdb) n
  4. 14              printf("%d\n", *p);
  5. (gdb) p a
  6. $8 = 100
  7. (gdb) p *p
  8. $9 = 100
  9. (gdb) p &a
  10. $10 = (int *) 0xbfffc310
  11. (gdb) p/x p
  12. $11 = 0xbfffc310
  13. (gdb) n
  14. 100
  15. 15              p++;
  16. (gdb) p *p
  17. $12 = 100
  18. (gdb) p/x p
  19. $13 = 0xbfffc310
  20. (gdb) n
  21. 16              printf("%d\n", *p);
  22. (gdb) p *p
  23. $14 = 200
  24. (gdb) p/x p
  25. $15 = 0xbfffc314
  26. (gdb) n
  27. 200
  28. 17              p++;
  29. (gdb) p *p
  30. $16 = 200
  31. (gdb) p/x p
  32. $17 = 0xbfffc314
  33. (gdb) n
  34. 18              printf("%d\n", *p);
  35. (gdb) p *p
  36. $18 = 300
  37. (gdb) p/x p
  38. $19 = 0xbfffc318
  39. (gdb) n
  40. 300
  41. 19      }
復(fù)制代碼
============================
test2
  1. Breakpoint 1, test2 (s=0x8048564 "hello world") at c.c:24
  2. 24              p = (int *)&s;
  3. (gdb) p *p
  4. $1 = 200
  5. (gdb) p/x p
  6. $2 = 0xbfffe588
  7. (gdb) n
  8. 25              p++;
  9. (gdb) p *p
  10. $3 = 134514020
  11. (gdb) p/x p
  12. $4 = 0xbfffe580
  13. (gdb) n
  14. 26              printf("%d\n", *p);
  15. (gdb) p *p
  16. $5 = 100
  17. (gdb) p/x p
  18. $6 = 0xbfffe584
  19. (gdb) n
  20. 100
  21. 27              p++;
  22. (gdb) p *p
  23. $7 = 100
  24. (gdb) p/x p
  25. $8 = 0xbfffe584
  26. (gdb) n
  27. 28              printf("%d\n", *p);
  28. (gdb) p *p
  29. $9 = 200
  30. (gdb) p/x p
  31. $10 = 0xbfffe588
  32. (gdb) n
  33. 200
  34. 29      }
  35. (gdb) p *p
  36. $11 = 200
  37. (gdb) p/x p
  38. $12 = 0xbfffe588
  39. (gdb) n
  40. main () at c.c:37
  41. 37              return 0;
  42. (gdb) n
  43. 38      }
  44. (gdb) n
  45. 0x00c44ebc in __libc_start_main () from /lib/libc.so.6
  46. (gdb) n
  47. Single stepping until exit from function __libc_start_main,
  48. which has no line number information.

  49. Program exited normally.
復(fù)制代碼

作者: cao627    時間: 2015-01-03 22:17
本帖最后由 cao627 于 2015-01-03 22:28 編輯
1.在test2中定義的是指向正整型指針p,對p++,p的步進(jìn)是4,而傳給test2的實參"hello world" 長度大于4個字節(jié),p++后指向的應(yīng)該還是在"hello.world"  這串字符存儲的空間內(nèi),怎么能指到參數(shù)100的存儲空間,視頻教程里確實做到了。



("hello world",100,200) 在內(nèi)存棧中開辟的是   存放helllo world字符串的首地址的空間,4字節(jié)空間,4字節(jié)空間
p首先取得是存放helllo world字符串的首地址的空間的地址。
對p++后就取到了存放helllo world字符串的首地址的空間后面的4字節(jié)空間


對于32位系統(tǒng),地址的長度是4字節(jié),上面的存放helllo world字符串的首地址的空間就是4字節(jié)。p是定義的是指向int型的,p++的步進(jìn)是4, 所以*(p++) 取得存放helllo world字符串的首地址的4字節(jié)空間的后面一個4字節(jié)的空間的內(nèi)容。

但是對于64位系統(tǒng),地址的長度是8字節(jié),上面的存放helllo world字符串的首地址的空間就是8字節(jié)。p是定義的是指向int型的,p++的步進(jìn)是4,所以*(P++)不能取到存放helllo world字符串的首地址的8字節(jié)空間后面一個4字節(jié)空間的內(nèi)容。

這可能就是我的系統(tǒng)(64位)上形參地址是從左往又依次遞減的緣故?
這樣:存放helllo world字符串的首地址的8字節(jié)空間的地址高于緊挨它的形參括號中它右邊4字節(jié)空間的地址,設(shè)形參括號中它右邊4字節(jié)空間的地址是A,由于這塊空間的大小為4字節(jié),所以存放helllo world字符串的首地址的8字節(jié)空間的地址就是A+4。這樣p取到存放helllo world字符串的首地址的8字節(jié)空間的地址,*(p--)就正好取到形參括號中它右邊4字節(jié)空間內(nèi)容。
作者: Dannysd    時間: 2015-01-03 22:39
回復(fù) 8# cao627


    默認(rèn)的是cdecl,從右至左壓參入棧

   要不加上 __attribute__((cdecl))這個再試試?
作者: zhaohongjian000    時間: 2015-01-04 09:45
取決于ABI規(guī)范,函數(shù)調(diào)用約定屬于ABI規(guī)范的一部分。簡單的說,現(xiàn)在還在用內(nèi)存?zhèn)鲄?shù)的,只有32位的x86了。
x86-64、arm這種都有完整的文檔,怎么傳參數(shù)查看文檔即可。

x86-64的附個鏈接:http://www.x86-64.org/documentation/abi.pdf
作者: idi0t    時間: 2015-01-04 17:26
回復(fù) 10# zhaohongjian000


    不明白什么叫“用內(nèi)存?zhèn)鲄?shù)”,怎么理解?
作者: zhaohongjian000    時間: 2015-01-04 17:29
回復(fù) 11# idi0t


    就是把參數(shù)放內(nèi)存里啊。一般就是放在stack上,從右向左。這么做的原因是為了支持可變參數(shù)的函數(shù),因為第一個參數(shù)的地址是在頂部。
作者: idi0t    時間: 2015-01-05 09:14
回復(fù) 12# zhaohongjian000


    那為什么說“簡單的說,現(xiàn)在還在用內(nèi)存?zhèn)鲄?shù)的,只有32位的x86了”,其它的是怎么傳的呢,能否簡單說下。
作者: zhaohongjian000    時間: 2015-01-05 11:05
回復(fù) 13# idi0t

放寄存器里。比如用8個寄存器用于參數(shù)傳遞,從左到右依次放到1到8號寄存器中。超過8個參數(shù)再使用內(nèi)存。返回值放到第一個寄存器中。
當(dāng)返回值尺寸比較大時(比如返回結(jié)構(gòu)體),有的ABI規(guī)定由caller分配內(nèi)存存放返回值,并把地址作為額外參數(shù)傳遞給callee。

有浮點運算部件的情況下,浮點參數(shù)一般是單獨放在浮點部件的寄存器中的。

總體方法都大體相似,但具體細(xì)節(jié)非常多。單單調(diào)用約定這一塊可能就有幾十種特殊情況。你可以找個ABI規(guī)范看一下,比如AMD64的。


x86(32位)的情況比較復(fù)雜,因為在發(fā)展早期沒有確定一個統(tǒng)一的ABI規(guī)范。各個編譯器、系統(tǒng)遵循的ABI都可能不一樣。而且由于多個編譯器在
x86(32位)上都采用棧來傳遞參數(shù),讓很多不明所以的人誤認(rèn)為參數(shù)天生就是要通過棧來傳遞的。
作者: yulihua49    時間: 2015-01-05 15:35
cao627 發(fā)表于 2015-01-03 10:34
看到一個視頻教程,里面用test2實現(xiàn)了:打印第二個參數(shù)和第三個參數(shù)的值,依據(jù)是形參的地址在內(nèi)存里緊挨著的 ...

X64,前6個形參在寄存器,你上哪兒找地址?
學(xué)一下ABI。
作者: idi0t    時間: 2015-01-05 15:53
回復(fù) 14# zhaohongjian000


    恩,謝謝解釋,沒接觸過64位的,學(xué)習(xí)了,突然之前無意中看過別人反匯編arm平臺下的程序好像也是用寄存器傳的。
作者: kaede_1    時間: 2015-01-05 16:39
LZ,有結(jié)果了嗎?我在我的環(huán)境下試了一下你的程序,調(diào)試如下:
  1. GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-51.el7
  2. Copyright (C) 2013 Free Software Foundation, Inc.
  3. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
  4. This is free software: you are free to change and redistribute it.
  5. There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
  6. and "show warranty" for details.
  7. This GDB was configured as "x86_64-redhat-linux-gnu".
  8. For bug reporting instructions, please see:
  9. <http://www.gnu.org/software/gdb/bugs/>...
  10. Reading symbols from /root/Project/programing/C/a.out...done.
  11. (gdb) b test2
  12. Breakpoint 1 at 0x4005f3: file func_args.c, line 16.
  13. (gdb) r
  14. Starting program: /root/Project/programing/C/a.out
  15. 100
  16. 200
  17. 300

  18. Breakpoint 1, test2 (s=0x400714 "hello world") at func_args.c:16
  19. 16                p = (int *)&s;
  20. Missing separate debuginfos, use: debuginfo-install glibc-2.17-55.el7.x86_64
  21. (gdb) n
  22. 17                p++;
  23. (gdb) p p
  24. $1 = (int *) 0x7fffffffdfa8
  25. (gdb) p *(p+8)
  26. $2 = 100
  27. (gdb) p *(p+10)
  28. $3 = 200
  29. (gdb) n
  30. 18                printf("%d\n", *p);
  31. (gdb) p p
  32. $4 = (int *) 0x7fffffffdfac
復(fù)制代碼
真心沒想明白阿




歡迎光臨 Chinaunix (http://www.72891.cn/) Powered by Discuz! X3.2