C++/C函数的调用规范

本文详细探讨了函数调用规范,包括__cdecl、__stdcall、__thiscall等,并通过实例展示了参数求值顺序如何影响函数调用结果。此外,还讨论了不同编译器对于参数压栈的不同实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

先看一个代码

void myfun(int i, int ii)
{
    cout << i << "    " << ii << endl;
}
void main()
{
    int b = 3;
    int arr[] = {6,7,8,9,10};
    int *ptr = arr;
    *(ptr++) += 123;
    printf("%d\t%d\n", *ptr, *++ptr);
    int i = 10;
    myfun(i, ++i);
}

VS2008输出:

0510

 

函数的调用规范

      函数的调用规范,也称为调用约定(Calling convention)。函数的调用规范决定了函数调用时,实参压栈、退栈及堆栈释放方式,以及函数名改编(Name Mangling)的方案,也即命名规范。

      Windows环境下常用的调用规范有:

     1)__cdecl:这是C/C++函数默认的调用规范,参数从右向左依次传递,压入堆栈,由调用函数负责堆栈的清退。这种方式适用于传递个数可变的参数给被调用函数,因为只有调用函数才知道它传递了多少个参数给被调函数。如printf函数。

     2)__stdcall:参数从右向左依次传递,并压入堆栈,由被调用函数清退堆栈。当函数有可变个数参数,自动转化为__cdecl调用规范。

     3)__thiscall:这是C++非静态成员函数的默认调用规范,不能使用个数可变的参数。调用非静态成员函数的时候,this指针直接保存在ECX寄存器中,不入栈。其他方面同__stdcall。

     4)__fastcall

      凡是接口函数都必须显示指定其调用规范,除非接口函数是类的非静态成员函数。

 

关于结果的解释

     据说此题是华为的笔试题。但可能出题者也没理解此种的奥秘,而只知道结果。关于函数调用的参数求值顺序,语言实现标准是未定义的。之所以出现如图所示的结果,不是由函数的调用规范决定的(从右向左对参数压栈),而是由微软编译器的具体实现:参数求值按照自右向左顺序决定的。函数调用压栈压入的是表达式的计算后的值,而不是表达式本身。

    另外,如果调用myfun(i, ++i); 改为myfun(i, i++);。将输入:11     10

 

进一步的困惑

   1: int main() 
   2: {
   3:     int i = 0;
   4:     printf( "%d, %d, %d, %d\n", i++, ++i, i, i++ );
   5:     return 0;
   6: }

     它在Visual C++ 2008的环境下编译时,输出结果为2, 3, 3, 0,在MinGW的GCC环境下编译运行时,输出结果则为2, 2, 1, 0。可见不同的编译器在实现时,对printf函数的参数进栈所作策略和抉择不同,导致了输出结果不同。结合上面关于对myfun的调用,发现即便在同一个编译器中,参数的求值顺序有时候也会不同。

     进一步学习,请参考卡http://fairymemory.net/2010/01/02/c语言函数压栈求值顺序/

转载于:https://www.cnblogs.com/younes/archive/2010/05/10/1732071.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值