关闭

__cdecl调用 C语言变参

1640人阅读 评论(0) 收藏 举报

__cdecl调用

在C语言中,函数调用支持不定参数,例如printf函数,可以不知道参数的个数,eg:

void err_info(const char *fmt, ...)

{

va_list ap;

va_start(ap, fmt);

vfprintf(stderr, fmt, ap);

va_end(ap);

}

这是如何实现的呢?如何支持变参的呢?

下面简要介绍下__cdecl调用方式,当然还有很多其他的调用方式,不过目前用不到

函数调用需要关注的几个方面:

1、函数调用时参数是如何保存的?

2、是调用者来清理函数调用还是被调用者来清理函数调用?

3、函数调用时参数的顺序是怎样的?

4、可能会用到哪些寄存器?

先说第一个问题,这个是很容易的,大家都知道C语言中是用栈来保存函数调用时的参数的

下面再分析其他几个问题:

在__cdcel标准中规定:

1、函数调用的参数入栈顺序是从右到左,即先压栈最右边的参数,最后压栈最左边的参数

2、在函数调用结束后用调用者来清理栈

3、调用过程中一般会用到eax、ebx、ecx等寄存器

void func(int a, int b)

{

...

}

int main(void)

{

...

func(1,2);

...

}

下面以这个例子来说明:

在调用的过程中由于是从右到左入栈,所以压栈的顺序是先2后1,用汇编可以描述为:

push 2

push 1

main函数对应的汇编大概是:

push 2

push 1

call func

add esp, 8 //清理栈,因为在调用过程中esp指针向下(或者说是向低地址)移动了8个字节

ret

这是调用顺序上

基于上面的几点我们就可以理解C语言中是如何实现变参函数的调用了:

1、由于函数调用是从右到左入栈的,因此函数调用时最左边的参数是最后一个入栈的,这样就可以函数调用时最左边的参数位置了

2、由于有format的指定,每个参数的大小是可知的,例如%d表示是int型,占用4个字节等

3、根据上面的两项就可以取出函数参数了。首先根据最左边参数(此处的含义是最后一个确定参数,也就是该参数后面的参数都是可变参数)

(例如

void func(char *arg1, char *arg2, ...)

那么最左边的参数意思就是arg2,因为它是最后一个确定参数

)

的位置可以找到函数调用时最左边的参数,然后根据每个参数的大小,移动esp指针(一般是向高地址移动),就可以依次取出其他所有参数了

wiki上介绍:http://en.wikipedia.org/wiki/X86_calling_conventions

clip_image001

clip_image002

clip_image003

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:335247次
    • 积分:3486
    • 等级:
    • 排名:第9478名
    • 原创:71篇
    • 转载:15篇
    • 译文:0篇
    • 评论:56条
    最新评论
    宽度拓展---一切知识