4. 一些过时的调用约定
__pascal, __fortran 和__syscall是三种已经过时的调用约定,MSDN的建议是使用WINAPI宏,也就是__stdcall来代替原来的PASCAL和 __far __pascal。
5. thiscall
在VS2005之前,这种调用约定仅仅应用于C++的成员函数:把this指针存放于CX寄存器,参数从右到左压栈,函数运行后,由函数来负责清除参数。我们前面已经讨论过由函数本身来清除参数的缺点:不支持可变参数个数的函数。所以对于那些可变参数个数的成员函数,C++使用的还是__cdecl调用约定。如下面这个class
f()成员函数使用的是thiscall调用约定,而v()成员函数使用的是__cdecl调用约定。还有一点需要注意,在VS2005之前,thiscall不能在程序中指定,因为它不是C++关键词。
在VS2005里,包括以后的VS版本中,__thiscall可以在托管VC程序中指定,它表明函数可以被原生代码调用。
6. __clrcall
看名字CLR call就知道这个调用约定和.NET Framework有关系,的确,使用__clrcall调用约定表明函数只能被托管代码(managed code)调用。如果您有一些VC++.NET的经验,可能会想:函数不是既可以被托管代码调用也可以被非托管代码(unmanaged code)调用吗?为什么要指定它只能被托管代码调用呢?
为了解释这个问题,必须介绍Double Thunking问题。先看下面一段代码,然后想想运行后会打印出什么结果。
#include <stdio.h> struct T { T(){}; T(const T&) { printf("copy constructor\n"); } ~T() { printf("destructor\n"); } }; struct S { virtual void f(T t) {}; } s; int main() { S* pS = &s; T t; printf("BEGIN\n"); pS->f(t); printf("END\n"); }
想好运行结果了吗?想好后让我们新建一个C++项目,输入这段代码,然后编译运行。有一点需要注意,我们先使用“No Common Language Runtime support”编译选项,这个选项告诉编译器按照我们以前的C++风格(比如:VC6,非托管的C++)进行编译。
