C语言函数调用约定
来源:优易学  2011-12-10 16:50:27   【优易学:中国教育考试门户网】   资料下载   IT书店


  fastcall
  fastcall调用约定和stdcall类似,它意味着:
  函数的第一个和第二个DWORD参数(或者尺寸更小的)通过ecx和edx传递,其他参数通过从右向左的顺序压栈
  被调用函数清理堆栈
  函数名修改规则同stdcall
  其声明语法为:int fastcall function(int a,int b)
  thiscall
  thiscall是唯一一个不能明确指明的函数修饰,因为thiscall不是关键字。它是C++类成员函数缺省的调用约定。由于成员函数调用还有一个this指针,因此必须特殊处理,thiscall意味着:
  参数从右向左入栈
  如果参数个数确定,this指针通过ecx传递给被调用者;如果参数个数不确定,this指针在所有参数压栈后被压入堆栈。
  对参数个数不定的,调用者清理堆栈,否则函数自己清理堆栈。

 为了说明这个调用约定,定义如下类和使用代码:
  class A
  {
  public:
  int function1(int a,int b);
  int function2(int a,...);
  };
  int A::function1 (int a,int b)
  {
  return a+b;
  }
  #include
  int A::function2(int a,...)
  {
  va_list ap;
  va_start(ap,a);
  int i;
  int result = 0;
  for(i = 0 ; i < a ; i ++)
  {
  result += va_arg(ap,int);
  }
  return result;
  }
  void callee()
  {
  A a;
  a.function1 (1,2);
  a.function2(3,1,2,3);
  }
  callee函数被翻译成汇编后就变成:
  //函数function1调用
  0401C1D push 2
  00401C1F push 1
  00401C21 lea ecx,[ebp-8]
  00401C24 call function1 注意,这里this没有被入栈
  //函数function2调用
  00401C29 push 3
  00401C2B push 2
  00401C2D push 1
  00401C2F push 3
  00401C31 lea eax,[ebp-8] 这里引入this指针
  00401C34 push eax
  00401C35 call function2
  00401C3A add esp,14h
  可见,对于参数个数固定情况下,它类似于stdcall,不定时则类似cdecl
  naked call
  这是一个很少见的调用约定,一般程序设计者建议不要使用。编译器不会给这种函数增加初始化和清理代码,更特殊的是,你不能用return返回返回值,只能用插入汇编返回结果。这一般用于实模式驱动程序设计,假设定义一个求和的加法程序,可以定义为:
  __declspec(naked) int add(int a,int b)
  {
  __asm mov eax,a
  __asm add eax,b
  __asm ret
  }
  注意,这个函数没有显式的return返回值,返回通过修改eax寄存器实现,而且连退出函数的ret指令都必须显式插入。上面代码被翻译成汇编以后变成:
  mov eax,[ebp+8]
  add eax,[ebp+12]
  ret 8
  注意这个修饰是和__stdcall及cdecl结合使用的,前面是它和cdecl结合使用的代码,对于和stdcall结合的代码,则变成:
  __declspec(naked) int __stdcall function(int a,int b)
  {
  __asm mov eax,a
  __asm add eax,b
  __asm ret 8 //注意后面的8
  }
  至于这种函数被调用,则和普通的cdecl及stdcall调用函数一致。
  函数调用约定导致的常见问题
  如果定义的约定和使用的约定不一致,则将导致堆栈被破坏,导致严重问题,下面是两种常见的问题:
  函数原型声明和函数体定义不一致
  DLL导入函数时声明了不同的函数约定
  以后者为例,假设我们在dll种声明了一种函数为:
  typedef int (*WINAPI DLLFUNC)func(int a,int b);
  hLib = LoadLibrary(...);
  DLLFUNC func = (DLLFUNC)GetProcAddress(...)//这里修改了调用约定
  result = func(1,2);//导致错误
  由于调用者没有理解WINAPI的含义错误的增加了这个修饰,上述代码必然导致堆栈被破坏,MFC在编译时插入的checkesp函数将告诉你,堆栈被破坏了。

上一页  [1] [2] 

责任编辑:小草

文章搜索:
 相关文章
热点资讯
资讯快报
热门课程培训