zoukankan      html  css  js  c++  java
  • cecel stacall 区别

        函数调用时需要用到栈(STACK,一种先入后出的存储结构)。当函数调用完成后,栈需要清除,这里就是问题的关键,如何清除?

        如果我们的函数使用了_cdecl,那么栈的清除工作是由调用者,用COM的术语来讲就是客户来完成的。这样带来了一个棘手的问题,不同的编译器产生栈的方式不尽相同,那么调用者能否正常的完成清除工作呢?答案是不能。

        如果使用__stdcall,上面的问题就解决了,函数自己解决清除工作。所以,在跨(开发)平台的调用中,我们都使用__stdcall。

        那么为什么还需要_cdecl呢?当我们遇到这样的函数如fprintf()它的参数是可变的,不定长的,被调用者事先无法知道参数的长度,事后的清除工作也无法正常的进行,因此,这种情况我们只能使用_cdecl。

        到这里我们有一个结论,如果你的程序中没有涉及可变参数,最好使用__stdcall关键字

     

    __cdecl

     

     

    __stdcall

     

    C 和 C++ 程序的缺省调用规范

     

    为了使用这种调用规范,需要你明确的加上 __stdcall (或 WINAPI )文字。即 return-type __stdcall function-name[(argument-list)]

     

     

    调用函数 (Callee) 返回,由调用方 (Caller) 调整堆栈。

     

    1. 调用方的函数调用

                                             

    2. 被调用函数的执行

     

    3. 被调用函数的结果返回

     

    4. 调用方清除调整堆栈

                                                                    

     

    调用函数 (Callee) 返回,由调用函数 (Callee) 调整堆栈。图示:

     

    1. 调用方的函数调用

     

    2. 被调用函数的执行

     

    3. 被调用函数清除调整堆栈

     

    4. 被调用函数的结果返回                                                           

     

    因为每个调用的地方都需要生成一段调整堆栈的代码,所以最后生成的文件较大。

     

     

    因为调整堆栈的代码只存在在一个地方(被调用函数的代码内),所以最后生成的文件较小。

     

    函数的参数个数可变(就像 printf 函数一样),因为只有调用者才知道它传给被调用函数几个参数,才能在调用结束时适当地调整堆栈。

     

     

    函数的参数个数不能是可变的。

     

    对于定义在 C 程序文件中的输出函数,函数名会保持原样,不会被修饰。

    对于定义在 C++ 程序文件中的输出函数,函数名会被修饰, MSDN 说 Underscore character (_) is prefixed to names . 我实际测试( VC4 和 VC6 )下来发现好像不是那么简单。

    可通过在前面加上 extern “C” 以去除函数名修饰。也可通过 .def 文件去除函数名修饰。

     

    不论是 C 程序文件中的输出函数还是 C++ 程序文件中的输出函数,函数名都会被修饰。

    对于定义在 C 程序文件中的输出函数, An underscore (_) is prefixed to the name. The name is followed by the at sign (@) followed by the number of bytes (in decimal) in the argument list.

    对于定义在 C++ 程序文件中的输出函数,好像更复杂,和 __cdecl 的情况类似。

    好像只能通过 .def 文件去除函数名修饰。

     

     

    _beginthread 需要 __cdecl 的线程函数地址

     

     

    _beginthreadex 和 CreateThread 需要 __stdcall 的线程函数地址

     

     

    两者的参数传递顺序都是从右向左。

    为了让 VB 可以调用,需要用 __stdcall 调用规范来定义 C/C++ 函数。请参看Microsoft KB153586 文章:How To Call C Functions That Use the _cdecl Calling Convention

    当你 LoadLibrary 一个 DLL 文件后, 把 GetProcAddress 取得的函数地址传给上面三个线程生成函数时,请务必确认实际定义在 DLL 文件的输出函数符合调用规范要求。否则,编译成 Release 版后运行,可能会破坏堆栈,程序行为不可预测。

    VC 中的相关编译开关:/Gd /Gr /Gz。另外,VC6中新增加的 /GZ 编译开关可以帮你检查堆栈问题。

    我也是初学者,若有不对的地方、可以补充的地方,请指教。谢谢。

  • 相关阅读:
    Python 面向对象(类与对象)
    修改Centos文 7件夹所在组及权限(不同用户共用一个文件夹)
    CGPoint,CGSize,CGRect
    关于投资币圈的思考和微信群问答干货整理
    数字货币合约交易基础知识
    比特币成长大事记
    健康医疗笔记(四)癌症知识
    ​阿德勒心理学《被讨厌的勇气》一切烦恼皆源于人际关系
    免费学术论文网站Sci-Hub
    数字货币交易所开发笔记3-撮合引擎开发
  • 原文地址:https://www.cnblogs.com/fensi/p/13308150.html
Copyright © 2011-2022 走看看