关于C语言参数入栈顺序的问题

2013-09-11 22:39:30 +08:00
 YUCOAT
源码如下:

https://gist.github.com/6524487

我的想法是,创建一个不参数定长的函数

void arg(int arg_count, ...)

调用者往往该函数内传入N + 1个参数,其中第一个参数的值为参数的个数。

arg()的功能就是将传入的int型参数输出到屏幕上。

我这样编写的依据是,C语言的参数是从右往左入栈的,然后知道第一个参数的地址和参数的个数之后就能将它所有的参数输出。

但是不巧的是,程序的执行结果是

0x7fff9e7f738c:4
0x7fff9e7f7390:-1635814512
0x7fff9e7f7394:32767
0x7fff9e7f7398:0
0x7fff9e7f739c:4

这是什么原因呢?
3531 次点击
所在节点    程序员
13 条回复
YUCOAT
2013-09-11 22:43:11 +08:00
编译器是Gcc 4.6x,不知道Windows下的vs编译器的结果是什么。

我阅读了一下stdarg.h的源码,想看看va_list/va_start的实现,但是被那些宏给绕晕了
SErHo
2013-09-11 22:47:41 +08:00
在Windows下使用gcc就是正常的,32位。
SErHo
2013-09-11 22:50:24 +08:00
Windows VS2012 也正常。
timonwong
2013-09-11 22:54:04 +08:00
x86_64头几个是使用的通用寄存器,其它的才是栈上的。
http://en.wikipedia.org/wiki/X86_calling_conventions
bengol
2013-09-12 00:01:29 +08:00
@timonwong 你这个是fastcall, lz的是cdecl
VYSE
2013-09-12 01:57:40 +08:00
楼主你是不是用了64位编译?内存地址都超了,64下就得用__int64一个个遍历了
timonwong
2013-09-12 07:56:09 +08:00
@bengol x86_64 有规定的abi,不是cdecl
onemoo
2013-09-12 08:46:55 +08:00
LZ试试不用这段“int* int_ptr = &count;”来取地址。
而是用汇编从%ebp开始向高址位去找找。

或者直接将你的这段代码汇编输出,看看int_ptr是不是指向调用帧的压栈区。

还有,不要用任何优化...
jiumingmao
2013-09-12 09:08:32 +08:00
用 va_start va_arg va_end 试试
LetFoxRun
2013-09-12 09:17:15 +08:00
pezy
2013-09-12 09:30:12 +08:00
完全取决于你用多少位的编译器。在Windows下用TDM-GCC 4.7.1 32位编译器,按照楼主的代码,就可以得到想要的答案。如果用64位,则需要将++int_ptr改为int_ptr += 2了,也是可以得到楼主想要的结果的。

我觉得这至少能证明楼主你的思路是对的。

当然比较成熟的方案还是使用va_start va_arg va_end了。
Jex
2013-09-12 11:52:33 +08:00
其实GCC在处理函数调用时,参数并不总是用栈传递的。

之前我回答过一个类似的问题:http://zhi.hu/VQVA

这里有不同的函数参数传递形式的列表:http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html

楼上有说什么fastcall与cdecl。楼主代码显然是在64位平台上编译的。
通过`gcc -S` 命令输出汇编代码,就会看到实际上参数是通过寄存器传递的
```
main:
.LFB1:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
movq %rsp, %rbp
.cfi_offset 6, -16
.cfi_def_cfa_register 6
movl $50, %r8d
movl $40, %ecx
movl $30, %edx
movl $20, %esi
movl $4, %edi
movl $0, %eax
call arg
movl $0, %eax
leave

```
johnnyb
2013-09-15 00:44:07 +08:00
看了一下汇编,确如楼上所说,是通过寄存传递的。va_start/va_end/va_arg 的实现也被"隐藏"起来了:

#define va_start(ap, param) __builtin_va_start(ap, param)
#define va_end(ap) __builtin_va_end(ap)
#define va_arg(ap, type) __builtin_va_arg(ap, type)

这几个 __builtin 是怎么实现的?

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/82119

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX