博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
调用约定与修饰名约定
阅读量:4204 次
发布时间:2019-05-26

本文共 2804 字,大约阅读时间需要 9 分钟。

   几乎每一种语言都有函数的概念,而作为函数就有参数,一般的说,参数的传递是通过堆栈来实现的(堆栈是一种先入后出的结构,使用Push压入,使用Pop弹出,Push和Pop必须成对使用),不过有两种不同的处理方法。
  一种方法是按原顺序把参数压入堆栈,然后使用CALL指令呼叫函数的地址,函数使用POP将参数弹出堆栈然后处理。由于堆栈的先入后出特性,所以这种方法对于调用者有利,因为被调用的函数得到的反序的参数。
  另一种方法相反,是按反序把参数压入堆栈,然后使用CALL指令呼叫函数的地址,函数使用POP将参数弹出堆栈然后处理。这种方法对于被调用者有利,因为被调用的函数得到的正序的参数。
  C语言和Pascal语言分别使用这两种方式,而Windows使用的调用方式和Pascal相同,所以以前的C程序编写Windows程序的时候需要使用关键字PASCAL指明使用Pascal调用规则;现在一般的不使用PASCAL关键字,而是使用_stdcall说明符,表明是一个标准调用。这种现在称为标准调用的就是第二种方式——反序压栈。
  函数调用约定有_stdcall、_cdcel、_fastcall、thiscall和naked call等多种,下面分别介绍如下:
  _stdcall:该调用约定相当于在16位DLL中经常使用的PASCAL调用约定。在32位的Visual C++中PASCAL调用约定不再被支持(实际上它已被定义为_stdcall,除_pascal外,_fortran和_syscall也不被支持),取而代之的是_stdcall调用约定。两者实质上是一致的,即函数的参数自右向左通过栈传递,由被调用函数在返回前负责清理传送参数的内存栈,不同的是函数名的修饰部分(有关函数名的修饰部分将在后面详细说明)。_stdcall也是PASCAL程序的缺省调用方式,多用在Win32 API函数中,函数采用从右到左的压栈方式,自己在退出时清空堆栈。Visual C++将函数编译后会在函数名前加上下划线前缀,在函数名后加上“@”和参数的字节数。
  _cdecl:即用_cdecl关键字说明的C调用约定。此调用约定是按照从右向左的顺序对参数来压栈的,由调用者负责参数的参数的出栈。对于传送参数的内存栈是由调用者来维护的(正因为如此,实现可变参数的函数只能使用该调用约定)。另外,在函数名修饰约定方面也有所不同。_cdecl是C和C++程序的默认调用方式。每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件会比调用_stdcall函数的要大。函数采用从右到左的压栈方式。VC将函数编译后会在函数名前面加上下划线前缀,这也是MFC默认的函数调用约定。
  _fastcall:该调用约定的主要特点就是速度快,因为它是通过寄存器来传送参数的(是用ECX和EDX寄存器传送前两个双字(DWORD)或更小的参数,剩下的参数仍旧自右向左压栈传送,被调用的函数在返回前清理传送参数的内存栈)。在函数名约定方面,它和前两者均不同。采用_fastcall调用约定的函数在编译后将在函数名前加上“@”前缀,在函数名后加上“@”和参数的字节数。
  thiscall:该调用约定仅应用于C++成员函数。this指针存放于CX寄存器,参数从右向左压栈。需要注意的是thiscall并非一个关键词,因此不能被程序员所指定。
  naked call:对于前面四种函数调用约定,如果必要,在进入函数时编译器会产生代码来保存ESI,EDI,EBX,EBP寄存器的内容并在退出函数时产生代码来进行恢复。而使用naked call调用约定的函数则不产生这样的代码。需要特别指出的是,naked call并非类型修饰符,必须和_declspec共同使用。
  关键字_stdcall、_cdecl和_fastcall均可以直接加在要输出的函数前,也可以在编译环境中设置。如果加在输出函数前的关键字与编译环境中的设置不同,则以所加的关键字为准。其对应的命令行参数分别为/Gz、/Gd和/Gr。默认状态为/Gd,即_cdecl。若要完全模仿PASCAL调用约定首先必须使用_stdcall调用约定,至于函数名修饰约定,可以通过其他方法模仿。还有一个值得一提的是WINAPI宏,在windows.h中有定义,它可以将输出函数翻译成适当的调用约定,在Win32中,它被定义为_stdcall。使用WINAPI宏可以创建自己的API函数。
  修饰名约定随调用约定和编译种类(C和C++)的不同而变化。下面分别就C编译时和C++编译时的修饰名约定规则进行介绍。
  (1)C编译时函数名修饰约定规则。
  _stdcall调用约定:在输出函数名前加上一个下划线前缀,后面加上一个“@”符号和其参数的字节数,格式为_functionname@number。
  _cdecl调用约定:仅在输出函数名前加上一个下划线前缀,格式为_functionname。
  _fastcall调用约定:在输出函数名前加上一个“@”符号,后面也是一个“@”符号和其参数的字节数,格式为@functionname@number。与PASCAL调用约定不同,它们均不改变输出函数名的字符大小写(PASCAL约定输出的函数名无任何修饰且全部大写)。
  (2)C++编译时函数名修饰约定规则。_stdcall调用约定:以“?”标识函数名的开始,后跟函数名。函数名后面以@@YG标志参数表的开始,后跟参数表。下面给出了参数表的代号表示。
  X void
  D char
  E unsigned char
  F short
  H int
  I unsigned int
  J long
  K unsigned long
  M float
  N double
  _N bool
  PA 指针。其后为指针类型,如相同的指针连续出现,则以0代替。
  参数表的第一项为该函数的返回值类型,其后依次为参数的数据类型。指针标识在其所指的数据类型前。在参数表后以@Z标识整个名字的结束,如果该函数没有参数,则以Z标识结束。其格式为?functionname@YG*****@Z或?functionname@@YG*XZ,例如:int Test1(char * var , unsigned long)表示为?Test1@@YGHPADK@Z,而void Test2()则表示为?Test2@@YGXXZ。
  对于_cdecl调用约定和_fastcall调用约定,规则同上面的_stdcall调用约定基本类似,只是参数表的开始标识由上面的@@YG分别变为@@YA和@@YI。Visual C++对函数的缺省声明是_cdecl,只能为C/C++所调用。

转载地址:http://mssli.baihongyu.com/

你可能感兴趣的文章
实现haproxy+LNMT负载均衡架构
查看>>
论文浅尝 | 通过共享表示和结构化预测进行事件和事件时序关系的联合抽取
查看>>
论文浅尝 | 融合多粒度信息和外部语言知识的中文关系抽取
查看>>
论文浅尝 | GMNN: Graph Markov Neural Networks
查看>>
廖雪峰Python教程 学习笔记3 hello.py
查看>>
从内核看epoll的实现(基于5.9.9)
查看>>
python与正则表达式
查看>>
安装.Net Framework 4.7.2时出现“不受信任提供程序信任的根证书中终止”的解决方法
查看>>
input type=“button“与input type=“submit“的区别
查看>>
解决Github代码下载慢问题!
查看>>
1.idea中Maven创建项目及2.对idea中生命周期的理解3.pom文件夹下groupId、artifactId含义
查看>>
LeetCode-栈|双指针-42. 接雨水
查看>>
stdin,stdout,stderr详解
查看>>
Linux文件和设备编程
查看>>
文件描述符
查看>>
终端驱动程序:几个简单例子
查看>>
登录linux密码验证很慢的解决办法
查看>>
fcntl函数总结
查看>>
HTML条件注释
查看>>
Putty远程服务器的SSH经验
查看>>