为什么函数指针的入参可以不等于函数原形的入参?——谈谈栈平衡

为什么函数指针的入参可以不等于函数原形的入参?——谈谈栈平衡,第1张

概述本文章向大家介绍为什么函数指针的入参可以不等于函数原形的入参?——谈谈栈平衡,主要包括为什么函数指针的入参可以不等于函数原形的入参?——谈谈栈平衡使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

【摘要】 最近在某产品推进再研版本和维护版本分支合一的事项,为有效的隔离新老特性,使用了BrIDge模式,其间采用了函数指针,在编译/运行时通过注册不同的具体实现函数,确定特性的具体行为。

一、问题

先尝试看看,下列代码的编译结果是什么?

typedef voID (*APP_FUNC)(unsigned , unsigned, unsigned);

voID Called(int i1, char s2)

{

      printf("%d,%c",i1,s2);

}

voID Callee()

{

      int i1 = -1024;

      char s2 = 'B';

      char s3 = 'C';

      APP_FUNC fp = (APP_FUNC) Called;

      fp(i1,s2,s3);

}

欢迎加入学习群【892643663】,获取全套免费C/C++企业实战级课程资源(素材+源码+视频)和编译大礼包

A. 不会出错,因为s3在实际函数Called中未使用。

B. 出错,因为入参数类型不匹配,函数指针入参为unsigned,实际执行函数Called入参为int,char。

C. 出错,因为入参个数不匹配,调用函数指针的入参为3个,实际执行函数Called入参为2个。

二、分析

我们知道C/C++中默认函数参数入栈顺序为从右至左,函数执行前参数入栈,函数完成后参数出栈;也就是说调用函数指针fp(i1,s3)时,参数入栈顺序为s3-> s2-> s1;那么在实际执行函数Called中,因为没有使用到s3,在默认字节对齐的情况下使用s1,s2变量时,unsigned占用的存储空间>=实际入参的存储空间,所以函数功能应该不会受影响。

问题是——Called执行结束时参数出栈,此种情况究竟应该出多少个呢?入栈出栈是否平衡呢?

    2.1 函数的调用约定

打开VC,我们可以看到有__cdecl、 __fastcall、 __stdcall 三种调用约定。通常

__cdecl   :C/C++默认的函数调用协议;

__stdcall :windows API默认的函数调用协议;

__fastcall:适用于对性能要求较高的场合,从左开始不大于4字节的参数放入cpu的ECX和EDX寄存器,其余参数从右向左入栈。

使用者可显式的标记函数的调用约定。

    2.2 三种调用方式的汇编展示    

对比__cdecl和__stdcall方式下的汇编,Callee调用Called函数前有明显的参数入栈动作(蓝色部分);但是在Called函数结束时,__cdecl直接ret并在调用函数Callee中add esp,0ch恢复栈顶,__stdcall则在函数内部ret 8(红色部分)恢复栈顶,也就是说__stdcall入栈12个字节,出栈8个字节,栈数据残留,后续运行结果未知。

在__fastcall方式下,Callee调用Called函数,从左向右i1,s2未入栈,通过寄存器ecx,edx传递,其余参数(s3)从右向左入栈后,调用Called函数;然后在Called函数中使用ret n恢复栈顶。本例中被调函数Called使用2个寄存器传递进来的参数,ret 0恢复栈顶,也就是说__fastcall入栈4个字节,出栈0个字节,栈数据残留。

栈内数据清除方式简单描述如下:

__cdecl   :函数调用结束后由函数调用者清除栈内数据。

__stdcall :函数调用结束后由被调用函数清除栈内数据。

__fastcall:函数调用结束后由被调用函数清除栈内数据。

三、标准遵从

在ISO/IEC 9899:1999 (E)中有如下描述:

A pointer to a function of one type may be converted to a pointer to a function of another type and back again; the result shall compare equal to the original pointer. If a converted pointer is used to call a function whose type is not compatible with the pointed-to type,the behavior is undefined.

四、总结

在标准中没有明确定义;虽然在当前既成事实的__cdecl方式下,函数指针和实现函数入参不同,我们定义的接口函数似乎能正确简洁的运行,但将来的事情谁说的准呢?有追求的程序员不会为未来埋坑。

作者|伍小川

欢迎加入学习群【892643663】,获取全套免费C/C++企业实战级课程资源(素材+源码+视频)和编译大礼包

总结

以上是内存溢出为你收集整理的为什么函数指针的入参可以不等于函数原形的入参?——谈谈栈平衡全部内容,希望文章能够帮你解决为什么函数指针的入参可以不等于函数原形的入参?——谈谈栈平衡所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/1264939.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-08
下一篇 2022-06-08

发表评论

登录后才能评论

评论列表(0条)

保存