zoukankan      html  css  js  c++  java
  • DWORD WINAPI?stdcall?

    网络编程实验2_(4)基于流式套接字的服务器程序设计中,创建了以下这个函数:

    DWORD WINAPI ClientThread(LPVOID lpParameter)

    以前我只学过类似:

    int swap(int x,int y)

    这样的函数,而这个函数看起来就很诡异,如何理解这个函数呢?


    DWORD是数据类型,在这里是返回值,返回32位数据。

    在visual studio中WINAPI转到定义可以看到:

    #define WINAPI __stdcall 

    也就是说WINAPI是一个宏,所代表的符号是__stdcall。 

    WINAPI是函数调用形式,windows API函数采用__stdcall标准调用约定,即由被调用函数来清理栈中的参数,这种方式是不能实现可变参数的。 

    __stdcall是函数调用约定的一种,函数调用约定主要约束了两件事:

    1.参数传递顺序

    2.调用堆栈由谁(调用函数或被调用函数)清理

    常见的函数调用约定:stdcall cdecl fastcall thiscall naked call

    __stdcall表示

    1.参数从右向左压入堆栈

    2.函数被调用者修改堆栈

    3.函数名(在编译器这个层次)自动加前导的下划线,后面紧跟一个@符号,其后紧跟着参数的尺寸

    在win32应用程序里,宏APIENTRY,WINAPI,都表示_stdcall,非常常见。


    摘自:

    __stdcall详解

    在C语言中,假设我们有这样的一个函数:int function(int a,int b)
    调用时只要用result = function(1,2)这样的方式就可以使用这个函数。但是,当高级语言被编译成计算机可以识别的机器码时,有一个问题就凸现出来:在CPU中,计算机没有办法知道一个函数调用需要多少个、什么样的参数,也没有硬件可以保存这些参数。也就是说,计算机不知道怎么给这个函数传递参数,传递参数的工作必须由函数调用者和函数本身来协调。为此,计算机提供了一种被称为栈的数据结构来支持参数传递。
    栈是一种先进后出的数据结构,栈有一个存储区、一个栈顶指针。栈顶指针指向堆栈中第一个可用的数据项(被称为栈顶)。用户可以在栈顶上方向栈中加入数据,这个操作被称为压栈(Push),压栈以后,栈顶自动变成新加入数据项的位置,栈顶指针也随之修改。用户也可以从堆栈中取走栈顶,称为弹出栈(pop),弹出栈后,栈顶下的一个元素变成栈顶,栈顶指针随之修改。函数调用时,调用者依次把参数压栈,然后调用函数,函数被调用以后,在堆栈中取得数据,并进行计算。函数计算结束以后,或者调用者、或者函数本身修改栈,使堆栈恢复原装。
    在参数传递中,有两个很重要的问题必须得到明确说明:
    当参数个数多于一个时,按照什么顺序把参数压入堆栈函数调用后,由谁来把堆栈恢复原装。在高级语言中,通过函数调用约定来说明这两个问题。常见的调用约定有:
    stdcall,cdecl,fastcall,thiscall,naked call


    为了深刻理解__stdcall,我查找了一些资料,参考链接如下:

    C++知识回顾之__stdcall、__cdcel和__fastcall三者的区别

    (这位老哥讲的很全面,但是格式有点乱。于是我稍微调整了一下格式)

    __stdcall、__cdecl和__fastcall是三种函数调用协议,函数调用协议会影响函数参数的入栈方式、栈内数据的清除方式、编译器函数名的修饰规则等。

     

    调用协议常用场合

    __stdcall:Windows API默认的函数调用协议。

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

    __fastcall:适用于对性能要求较高的场合

    函数参数入栈方式

    __stdcall:函数参数由右向左入栈。

    __cdecl:函数参数由右向左入栈。

    __fastcall:从左开始不大于4字节的参数放入CPU的ECX和EDX寄存器,其余参数从右向左入栈。

     

    栈内数据清除方式

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

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

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

    (1)不同编译器设定的栈结构不尽相同,跨开发平台时由函数调用者清除栈内数据不可行。

    (2)某些函数的参数是可变的,如printf函数,这样的函数只能由函数调用者清除栈内数据。

    (3)由调用者清除栈内数据时,每次调用都包含清除栈内数据的代码,故可执行文件较大。

     

    C语言编译器函数名称修饰规则

    __stdcall:编译后,函数名被修饰为“_functionname@number”。

    __cdecl:编译后,函数名被修饰为“_functionname”。

    __fastcall:编译后,函数名给修饰为“@functionname@nmuber”。

    注:“functionname”为函数名,“number”为参数字节数。

    注:函数实现和函数定义时如果使用了不同的函数调用协议,则无法实现函数调用。

     

    C++语言编译器函数名称修饰规则

    __stdcall:编译后,函数名被修饰为“?functionname@@YG******@Z”。

    __cdecl:编译后,函数名被修饰为“?functionname@@YA******@Z”。

    __fastcall:编译后,函数名被修饰为“?functionname@@YI******@Z”

     

    注:“******”为函数返回值类型和参数类型表。

    注:函数实现和函数定义时如果使用了不同的函数调用协议,则无法实现函数调用。

    C语言和C++语言间如果不进行特殊处理,也无法实现函数的互相调用。

  • 相关阅读:
    批处理向FTP上传指定属性的文件 批处理增量备份的例子
    基于PHPExcel常用方法总结(phpexcel类库实例)
    Expo大作战(三十四)--expo sdk api之LinearGradient(线性渐变),KeepAwake(保持屏幕不休眠),IntentLauncherAndroid,Gyroscope,
    Expo大作战(三十三)--expo sdk api之MapView(地图),MailComposer(磁力传感计),Lottie(动画)
    Expo大作战(三十二)--expo sdk api之Noifications
    Expo大作战(三十一)--expo sdk api之Payments(expo中的支付),翻译这篇文章傻逼了,完全不符合国内用户,我只负责翻译大家可以略过!
    Expo大作战(三十)--expo sdk api之Permissions(权限管理模块),Pedometer(计步器api)
    生成分类编号的方法
    牛腩学Kotlin做Android应用
    我用Xamarin开发android应用,应用在真机上一打开就退出了
  • 原文地址:https://www.cnblogs.com/cyx-b/p/12555128.html
Copyright © 2011-2022 走看看