免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 4287 | 回复: 16
打印 上一主题 下一主题

[C] c语言函数形参的地址关系 [复制链接]

论坛徽章:
6
摩羯座
日期:2013-08-24 10:43:10狮子座
日期:2013-08-25 10:27:06天秤座
日期:2013-09-11 20:28:44午马
日期:2014-09-28 16:06:0015-16赛季CBA联赛之八一
日期:2016-12-19 13:55:0515-16赛季CBA联赛之天津
日期:2016-12-20 14:01:23
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2015-01-03 10:34 |只看该作者 |倒序浏览
本帖最后由 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. }

复制代码
看到一个视频教程,里面用test2实现了:打印第二个参数和第三个参数的值,依据是形参的地址在内存里紧挨着的,但在我的环境里无法实现这一点。

问题:
1.在test2中定义的是指向正整型指针p,对p++,p的步进是4,而传给test2的实参"hello world" 长度大于4个字节,p++后指向的应该还是在"hello.world"  这串字符存储的空间内,怎么能指到参数100的存储空间,视频教程里确实做到了。

2.在我的环境里test1的参数的地址关系是从左往右递减的,即如上代码是用p--实现依次打印第一,第二,第三个参数。而在视频教程中是用p++。这跟编译器不同的关系吗?

论坛徽章:
6
酉鸡
日期:2013-11-04 15:30:02巳蛇
日期:2014-01-23 10:36:23双鱼座
日期:2014-01-23 13:08:332015亚冠之鹿岛鹿角
日期:2015-09-03 14:36:002015亚冠之武里南联
日期:2015-09-18 10:48:1315-16赛季CBA联赛之山西
日期:2016-05-05 00:05:33
2 [报告]
发表于 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. }
复制代码
  1. ./a.out
  2. 100
  3. 200
  4. 300
  5. 100
  6. 200
复制代码

论坛徽章:
6
酉鸡
日期:2013-11-04 15:30:02巳蛇
日期:2014-01-23 10:36:23双鱼座
日期:2014-01-23 13:08:332015亚冠之鹿岛鹿角
日期:2015-09-03 14:36:002015亚冠之武里南联
日期:2015-09-18 10:48:1315-16赛季CBA联赛之山西
日期:2016-05-05 00:05:33
3 [报告]
发表于 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   
复制代码
  1. 8048405:       83 45 fc 04             addl   $0x4,0xfffffffc(%ebp)
复制代码
这句对应的是p++,其实是在栈上向移动到了下一个地址

论坛徽章:
6
摩羯座
日期:2013-08-24 10:43:10狮子座
日期:2013-08-25 10:27:06天秤座
日期:2013-09-11 20:28:44午马
日期:2014-09-28 16:06:0015-16赛季CBA联赛之八一
日期:2016-12-19 13:55:0515-16赛季CBA联赛之天津
日期:2016-12-20 14:01:23
4 [报告]
发表于 2015-01-03 19:45 |只看该作者
对于test1这样的形式,在我的环境中要用指针递减的方式才能依次打印各个参数。
对于test2这样的形式,在我的环境中无论用指针递增还是递减都不能打印字符串长量后的各个参数。

我的环境是
ubuntu 14.04  64位
gcc 4.8.2

论坛徽章:
6
摩羯座
日期:2013-08-24 10:43:10狮子座
日期:2013-08-25 10:27:06天秤座
日期:2013-09-11 20:28:44午马
日期:2014-09-28 16:06:0015-16赛季CBA联赛之八一
日期:2016-12-19 13:55:0515-16赛季CBA联赛之天津
日期:2016-12-20 14:01:23
5 [报告]
发表于 2015-01-03 19:53 |只看该作者
本帖最后由 cao627 于 2015-01-03 19:54 编辑

@Dannysd
请你在test2中打印一下p取到字符串常量首地址的地址,然后再打印一下p++后的地址。看看两地址的差。

论坛徽章:
324
射手座
日期:2013-08-23 12:04:38射手座
日期:2013-08-23 16:18:12未羊
日期:2013-08-30 14:33:15水瓶座
日期:2013-09-02 16:44:31摩羯座
日期:2013-09-25 09:33:52双子座
日期:2013-09-26 12:21:10金牛座
日期:2013-10-14 09:08:49申猴
日期:2013-10-16 13:09:43子鼠
日期:2013-10-17 23:23:19射手座
日期:2013-10-18 13:00:27金牛座
日期:2013-10-18 15:47:57午马
日期:2013-10-18 21:43:38
6 [报告]
发表于 2015-01-03 20:22 |只看该作者
研究这个意义不大,不具有可移植性

论坛徽章:
6
酉鸡
日期:2013-11-04 15:30:02巳蛇
日期:2014-01-23 10:36:23双鱼座
日期:2014-01-23 13:08:332015亚冠之鹿岛鹿角
日期:2015-09-03 14:36:002015亚冠之武里南联
日期:2015-09-18 10:48:1315-16赛季CBA联赛之山西
日期:2016-05-05 00:05:33
7 [报告]
发表于 2015-01-03 20:57 |只看该作者
本帖最后由 Dannysd 于 2015-01-03 20:58 编辑

我是32位环境,gcc version 4.1.2 Centos
可变参数这个没弄懂。。。。
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      }
复制代码
============================
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.
复制代码

论坛徽章:
6
摩羯座
日期:2013-08-24 10:43:10狮子座
日期:2013-08-25 10:27:06天秤座
日期:2013-09-11 20:28:44午马
日期:2014-09-28 16:06:0015-16赛季CBA联赛之八一
日期:2016-12-19 13:55:0515-16赛季CBA联赛之天津
日期:2016-12-20 14:01:23
8 [报告]
发表于 2015-01-03 22:17 |只看该作者
本帖最后由 cao627 于 2015-01-03 22:28 编辑
1.在test2中定义的是指向正整型指针p,对p++,p的步进是4,而传给test2的实参"hello world" 长度大于4个字节,p++后指向的应该还是在"hello.world"  这串字符存储的空间内,怎么能指到参数100的存储空间,视频教程里确实做到了。



("hello world",100,200) 在内存栈中开辟的是   存放helllo world字符串的首地址的空间,4字节空间,4字节空间
p首先取得是存放helllo world字符串的首地址的空间的地址。
对p++后就取到了存放helllo world字符串的首地址的空间后面的4字节空间


对于32位系统,地址的长度是4字节,上面的存放helllo world字符串的首地址的空间就是4字节。p是定义的是指向int型的,p++的步进是4, 所以*(p++) 取得存放helllo world字符串的首地址的4字节空间的后面一个4字节的空间的内容。

但是对于64位系统,地址的长度是8字节,上面的存放helllo world字符串的首地址的空间就是8字节。p是定义的是指向int型的,p++的步进是4,所以*(P++)不能取到存放helllo world字符串的首地址的8字节空间后面一个4字节空间的内容。

这可能就是我的系统(64位)上形参地址是从左往又依次递减的缘故?
这样:存放helllo world字符串的首地址的8字节空间的地址高于紧挨它的形参括号中它右边4字节空间的地址,设形参括号中它右边4字节空间的地址是A,由于这块空间的大小为4字节,所以存放helllo world字符串的首地址的8字节空间的地址就是A+4。这样p取到存放helllo world字符串的首地址的8字节空间的地址,*(p--)就正好取到形参括号中它右边4字节空间内容。

论坛徽章:
6
酉鸡
日期:2013-11-04 15:30:02巳蛇
日期:2014-01-23 10:36:23双鱼座
日期:2014-01-23 13:08:332015亚冠之鹿岛鹿角
日期:2015-09-03 14:36:002015亚冠之武里南联
日期:2015-09-18 10:48:1315-16赛季CBA联赛之山西
日期:2016-05-05 00:05:33
9 [报告]
发表于 2015-01-03 22:39 |只看该作者
回复 8# cao627


    默认的是cdecl,从右至左压参入栈

   要不加上 __attribute__((cdecl))这个再试试?

论坛徽章:
12
巳蛇
日期:2013-09-16 15:32:242015年辞旧岁徽章
日期:2015-03-03 16:54:152015年亚洲杯之约旦
日期:2015-02-11 14:38:37双鱼座
日期:2015-01-05 11:05:47戌狗
日期:2014-12-08 09:41:18戌狗
日期:2014-08-15 09:29:29双子座
日期:2014-08-05 09:17:17卯兔
日期:2014-06-08 15:32:18巳蛇
日期:2014-01-27 08:47:08白羊座
日期:2013-11-28 21:04:15巨蟹座
日期:2013-11-13 21:58:012015年亚洲杯之科威特
日期:2015-04-17 16:51:51
10 [报告]
发表于 2015-01-04 09:45 |只看该作者
取决于ABI规范,函数调用约定属于ABI规范的一部分。简单的说,现在还在用内存传参数的,只有32位的x86了。
x86-64、arm这种都有完整的文档,怎么传参数查看文档即可。

x86-64的附个链接:http://www.x86-64.org/documentation/abi.pdf
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP