一、概念
1)_stdcall调用
   _stdcall是Pascal程序的缺省调用方式,参数采用从右到左的压栈方式,由调用者完成压栈操作,被调函数自身在返回前清空堆栈。
   WIN32 Api都采用_stdcall调用方式,这样的宏定义说明了问题:  #define WINAPI _stdcall
  
   按C编译方式,_stdcall调用约定在输出函数名前面加下划线,后面加“@”符号和参数的字节数,形如_functionname@number.
   Therefore, the function declared as int func( int a, double b ) is decorated as follows: _func@12.
   按C++编译方式,可参看(三)

2)_cdecl调用
   _cdecl是C/C++的缺省调用方式,参数采用从右到左的压栈方式,由调用者完成压栈操作
,传送参数的内存栈由调用者维护。
   _cedcl约定的函数只能被C/C++调用,每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用_stdcall函数的大。
   按C编译方式,_cdecl调用约定仅在输出函数名前面加下划线,形如_functionname。
   按C++编译方式,可参看(三)

二、区别
     几乎我们写的每一个WINDOWS API函数都是__stdcall类型的,首先,需要了解两者之间的区别:
WINDOWS的函数调用时需要用到栈(STACK,一种先入后出的存储结构)。当函数调用完成后,栈需要清除,这里就是问题的关键,如何清除?如果我
们的函数使用了_cdecl,那么栈的清除工作是由调用者,用COM的术语来讲就是客户来完成的。这样带来了一个棘手的问题,不同的编译器产生栈的方式不
尽相同,那么调用者能否正常的完成清除工作呢?答案是不能。如果使用__stdcall,上面的问题就解决了,函数自己解决清除工作。所以,在跨(开发)
平台的调用中,我们都使用__stdcall(虽然有时是以WINAPI的样子出现)。那么为什么还需要_cdecl呢?当我们遇到这样的函数如
fprintf()它的参数是可变的,不定长的,被调用者事先无法知道参数的长度,事后的清除工作也无法正常的进行,因此,这种情况我们只能使用
_cdecl。到这里我们有一个结论,如果你的程序中没有涉及可变参数,最好使用__stdcall关键字。

三、编译时函数名修饰约定规则
1)
首先介绍一下函数修饰名,C”
或者“C++”函数在内部(编译和链接)通过修饰名识别。修饰名是编译器在编译函数定义或者原型时生成的字符串。有些情况下使用函数的修饰名是必要的,如
在模块定义文件里头指定输出“C++”重载函数、构造函数、析构函数,又如在汇编代码里调用“C””或“C++”函数等。修饰名由函数名、类名、调用约
定、返回类型、参数等共同决定。名字修饰约定随调用约定和编译种类(C或C++)的不同而变化。函数名修饰约定随编译种类和调用约定的不同而不同。下面详
细说明在C++编译器中修饰名的约定方法,在C编译器中的变化已经在(一)中阐述。

2)C++编译时函数名修饰约定规则
    __stdcall调用约定:
  1)、以"?"标识函数名的开始,后跟函数名;
  2)、函数名后面以"@@YG"标识参数表的开始,后跟参数表;
  3)、参数表以代号表示:
  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"代替,一个"0"代表一次重复;
  4)、参数表的第一项为该函数的返回值类型,其后依次为参数的数据类型,指针标识在其所指数据类型前;
  5)、参数表后以"@Z"标识整个名字的结束,如果该函数无参数,则以"Z"标识结束。
  其格式为"?functionname@@YG*****@Z"或"?functionname@@YG*XZ",例如   int Test1(char *var1,unsigned long)-----“?Test1@@YGHPADK@Z”   void Test2() -----“?Test2@@YGXXZ
  __cdecl调用约定:
  规则同上面的_stdcall调用约定,只是参数表的开始标识由上面的"@@YG"变为"@@YA"。

四、在C++编译时,引入c库里面的函数的处理
   
如果Add(int a, int
b)是在c语言编译器编译,而在c++文件使用,由(三)知,因为c编译器和c++编译器对函数名的解释不一样(c++编译器解释函数名的时候要考虑函数
参数,这样是了方便函数重载,而在c语言中不存在函数重载的问题),会出现链接错误。 哪么该如何处理呢?这是关键字extern就派上用场了。
   而在c++文件使用c编译器编译过的函数,则需要在c++文件中声明:extern "C" Add(int a, int b),使用extern "C",实质就是告诉c++编译器,该函数是c库里面的函数。请保持我的名称,不要给我生成用于链接的函数修饰名。

另外顺便说一下extern的另一个用途,extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在
其他模块中寻找其定义。#inlcude "***.***"     和 int   f()  等价于   extern int f() ;即
extern 申明的函数前不需要加引用头文件就能用。

参考文献:
博客:http://www.cnblogs.com/Winston/archive/2008/09/11/1289391.html

函数调用的区别:_cdecl以及_stdcall的更多相关文章

  1. 转:函数调用的区别:_cdecl以及_stdcall

    函数调用的几个概念:_stdcall,_cdecl.... 1._stdcall是Pascal程序的缺省调用方式,通常用于Win32 Api中,函数采用从右到左的压栈方式,自己在退出时清空堆栈.VC将 ...

  2. _cdecl与_stdcall区别

    _cdecl与_stdcall是最常用的的两种函数调用修饰,区别在于函数返回时,清理栈(恢复栈平衡)是caller做还是被调函数做. : _cdecl int add1(int a, int b) : ...

  3. C++编译器详解(三)函数调用的区别:_cdecl以及_stdcall

    1._stdcall是Pascal程序的缺省调用方式,通常用于Win32 API中,函数采用从右到左的压栈方式,自己在退出时清空堆栈.VC将函数编译后会在函数名前面加上下划线前缀,在函数名后加上&qu ...

  4. _cdecl 与 _stdcall 区别

    前段时间编程时遇到过这么一个问题,我写了一个DLL,把里面的一个函数导出来,然后再定义一个签名与其匹配的函数指针,动态地把这个DLL加载起来(LoadLibrary),得到函数指针后,一调用,结果报错 ...

  5. 函数调用方法之__cdecl与_stdcall

    在debug VS c工程文件时,碰到cannot convert from 'int (__cdecl *)(char *)' to 'xxx'这个问题,发现问题在于typedef函数指针类型时,将 ...

  6. python中方法调用和函数调用的区别

    函数调用: 传几个参数,就会有几个实参方法调用: 默认传递一个参数self,至少要定义一个形参

  7. C++ 普通函数和虚函数调用的区别

    引出:写个类A,声明类A指针指向NULL,调用类A的方法会有什么后果,编译通过吗,运行会通过吗? #include<stdio.h> #include<iostream> us ...

  8. 第05课:GDB常用命令详解(中)

    本科核心内容: info和thread命令 next.step.util.finish和return命令 5.1info和thread命令 在前面使用info break命令查看当前断点时介绍过,in ...

  9. 第06课:GDB 常用命令详解(中)

    本课的核心内容: info 和 thread 命令 next.step.util.finish.return 和 jump 命令 info 和 thread 命令 在前面使用 info break 命 ...

随机推荐

  1. js 原型链与继承

    var A = function(){ this.name="xiaoming"; } A.prototype.age=9; var a = new A(); console.lo ...

  2. redis CentOS6.5安装及集群部署

    .下载redis source包 链接:https://pan.baidu.com/s/122ZCjNvjl9Jx6M2YsLrncw 密码:92ze 2.解压 tar -xzf redis-3.2. ...

  3. [Hadoop]Hadoop章1 Hadoop原理解析

    Hadoop是Apache软件基金会所开发的并行计算框架与分布式文件系统.最核心的模块包括Hadoop Common.HDFS与MapReduce. HDFS HDFS是Hadoop分布式文件系统(H ...

  4. AX_UserInfo

    UserInfoHelp::userInUserGroup(curuserid(), "Admin")  EmplTable::userId2EmplId(curuserid()) ...

  5. SIFT算法

     备注:源代码还未理解,所以未附上——下周任务 一.SIFT算法 1.算法简介 尺度不变特征转换即SIFT (Scale-invariant feature transform)是一种计算机视觉的算法 ...

  6. JavaScript:再谈Tasks和Microtasks

    JavaScript是单线程,也就是说JS的堆栈中只允许有一类任务在执行,不可以同时执行多类任务.在读js文件时,所有的同步任务是一条task,当然了,每一条task都是一个队列,按顺序执行.而如果在 ...

  7. SGTtrick

    SGTtrick By 蒟蒻 ldxoiBy\ 蒟蒻\ ldxoiBy 蒟蒻 ldxoi Chapter 1.关于线段树操作的一些分析 我们知道,线段树有两个核心的函数pushdownpushdown ...

  8. python_requests随笔

    #coding=utf-8 import requests url = "http://oj.jxust.edu.cn" #如果需要持续的链接,可以使用里面的Session方法(保 ...

  9. MySQL常用内置变量

      MySQL用很多常用的内置变量,掌握这些内置变量后对于我们快速获取当前MySQL的配置有很大帮助,下面就来列举几个常用的变量. 查看当前MySQL版本号信息.show variables like ...

  10. Kaldi如何准备自己的数据

    Introduction 跑完kaldi的一些脚本例子,你可能想要自己用Kaldi跑自己的数据集.这里将会阐述如何准备好数据. run.sh较上的部分是有关数据准备的,通常local与数据集相关. 例 ...