C/C++调用约定
2016-11-13 14:55
197 查看
C/C++函数调用约定
在编程中,一个函数完整的执行需要经过编译链接等多个过程,而在每个过程中编译器都需要为程序提供不同的服务,那么一个函数的调用执行到底需要几个过程呢?下面我们先通过一个函数栈帧的创建看看。
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
int Add(int left,
int right)
{
return left +
right;
}
int main()
{
int a, b;
int ret = 0;
scanf("%d%d", &a, &b);
ret = Add(a, b);
system("system");
return 0;
}
在这个程序中,我们来通过Add()函数的内部汇编代码来看看整个函数的执行过程:
下面我们用语言描述一下整个过程:
1. 为函数开辟空间
2. 初始化已开辟空间
3. 把函数参数压栈
4. 执行函数
5. 处理函数返回值
6. 对于第3步中压栈的那些寄存器,恢复它们原来的值
7. 根据不同的调用约定,清除第1步中压栈的参数,然后返回,或者先返回然后清除。
可以看到第6步是第3步的逆操作,而第7步是第1,2步的逆操作,在第3步中对函数的参数进行压栈,那么当参数个数多于一个时,编译器会按照什么顺序把参数压入栈的?而在第6、7步中又是怎么把堆栈恢复原装?这就引出了我们今天要讨论的题目——调用约定。
首先我们先来看看常用的几种调用约定:
在C语言中有: __cdecl、__stdcall、__fastcall、naked、__pascal
在C++中有: __cdecl、__stdcall、__fastcall、naked、__pascal、_thiscall
VC 中默认调用是 __cdecl 方式,Windows
API 使用 __stdcall 调用方式,在 DLL 导出函数中,为了跟Windows
API 保持一致,建议使用 __stdcall 方式。
在VC中,可以设置默认的调用约定,设置路径为:Project à Properties à Configuration
Properties à C/C++ à Advanced à call
conversion
下面我们就来详细介绍一下这六种调用约定:
1、__cdecl
__cdecl调用约定又称为 C 调用约定,是 C/C++ 语言缺省的调用约定。参数按照从右至左的方式入栈,函数本身不清理栈,此工作由调用者负责,返回值在EAX中。由于由调用者清理栈,所以允许可变参数函数存在,如int sprintf(char* buffer,const char* format,...);。
2、__stdcall
__stdcall 很多时候被称为 pascal 调用约定。pascal 语言是早期很常见的一种教学用计算机程序设计语言,其语法严谨。参数按照从右至左的方式入栈,函数自身清理堆栈,返回值在EAX中。
3、__fastcall
顾名思义,__fastcall 的特点就是快,因为它通过 CPU 寄存器来传递参数。他用 eax 和 edx 传送前两个双字(DWORD)或更小的参数,剩下的参数按照从右至左的方式入栈,函数自身清理堆栈,返回值在 EAX 中。
4、naked
naked 是一个很少见的调用约定,一般不建议使用。编译器不会给这种函数增加初始化和清理代码,更特殊的是,你不能用return返回返回值,只能用插入汇编返回结果,此调用约定必须跟 __declspec 同时使用。例如定义一个求和程序,如__declspec(naked) int add(int a,int b);。
5、__pascal
这是 pascal 语言的调用约定,跟 __stdcall 一样,参数按照从右至左的方式入栈,函数自身清理堆栈,返回值在eax中。VC 中已经废弃了这种调用方式,因此在写 VC 程序时,建议使用 __stdcall 代替。
6、__thiscall
这是 C++ 语言特有的一种调用方式,用于类成员函数的调用约定。如果参数确定,this 指针存放于 ecx 寄存器,函数自身清理堆栈;如果参数不确定,this指针在所有参数入栈后再入栈,调用者清理栈。__thiscall 不是关键字,程序员不能使用。参数按照从右至左的方式入栈。
在编程中,一个函数完整的执行需要经过编译链接等多个过程,而在每个过程中编译器都需要为程序提供不同的服务,那么一个函数的调用执行到底需要几个过程呢?下面我们先通过一个函数栈帧的创建看看。
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
int Add(int left,
int right)
{
return left +
right;
}
int main()
{
int a, b;
int ret = 0;
scanf("%d%d", &a, &b);
ret = Add(a, b);
system("system");
return 0;
}
在这个程序中,我们来通过Add()函数的内部汇编代码来看看整个函数的执行过程:
下面我们用语言描述一下整个过程:
1. 为函数开辟空间
2. 初始化已开辟空间
3. 把函数参数压栈
4. 执行函数
5. 处理函数返回值
6. 对于第3步中压栈的那些寄存器,恢复它们原来的值
7. 根据不同的调用约定,清除第1步中压栈的参数,然后返回,或者先返回然后清除。
可以看到第6步是第3步的逆操作,而第7步是第1,2步的逆操作,在第3步中对函数的参数进行压栈,那么当参数个数多于一个时,编译器会按照什么顺序把参数压入栈的?而在第6、7步中又是怎么把堆栈恢复原装?这就引出了我们今天要讨论的题目——调用约定。
首先我们先来看看常用的几种调用约定:
在C语言中有: __cdecl、__stdcall、__fastcall、naked、__pascal
在C++中有: __cdecl、__stdcall、__fastcall、naked、__pascal、_thiscall
VC 中默认调用是 __cdecl 方式,Windows
API 使用 __stdcall 调用方式,在 DLL 导出函数中,为了跟Windows
API 保持一致,建议使用 __stdcall 方式。
在VC中,可以设置默认的调用约定,设置路径为:Project à Properties à Configuration
Properties à C/C++ à Advanced à call
conversion
下面我们就来详细介绍一下这六种调用约定:
1、__cdecl
__cdecl调用约定又称为 C 调用约定,是 C/C++ 语言缺省的调用约定。参数按照从右至左的方式入栈,函数本身不清理栈,此工作由调用者负责,返回值在EAX中。由于由调用者清理栈,所以允许可变参数函数存在,如int sprintf(char* buffer,const char* format,...);。
2、__stdcall
__stdcall 很多时候被称为 pascal 调用约定。pascal 语言是早期很常见的一种教学用计算机程序设计语言,其语法严谨。参数按照从右至左的方式入栈,函数自身清理堆栈,返回值在EAX中。
3、__fastcall
顾名思义,__fastcall 的特点就是快,因为它通过 CPU 寄存器来传递参数。他用 eax 和 edx 传送前两个双字(DWORD)或更小的参数,剩下的参数按照从右至左的方式入栈,函数自身清理堆栈,返回值在 EAX 中。
4、naked
naked 是一个很少见的调用约定,一般不建议使用。编译器不会给这种函数增加初始化和清理代码,更特殊的是,你不能用return返回返回值,只能用插入汇编返回结果,此调用约定必须跟 __declspec 同时使用。例如定义一个求和程序,如__declspec(naked) int add(int a,int b);。
5、__pascal
这是 pascal 语言的调用约定,跟 __stdcall 一样,参数按照从右至左的方式入栈,函数自身清理堆栈,返回值在eax中。VC 中已经废弃了这种调用方式,因此在写 VC 程序时,建议使用 __stdcall 代替。
6、__thiscall
这是 C++ 语言特有的一种调用方式,用于类成员函数的调用约定。如果参数确定,this 指针存放于 ecx 寄存器,函数自身清理堆栈;如果参数不确定,this指针在所有参数入栈后再入栈,调用者清理栈。__thiscall 不是关键字,程序员不能使用。参数按照从右至左的方式入栈。
相关文章推荐
- __declspec(dllimport)和__declspec(dllexport)的区别,以及有关c/c++调用约定
- C++调用约定和名字约定
- __declspec(dllimport)和__declspec(dllexport)的区别,以及有关c/c++调用约定
- C++调用约定和名字约定(转)
- C++ 调用C#函数 并指定函数的调用约定
- C++编译时 函数调用约定 与 函数名修饰约定规则
- C/C++:函数的编译方式与调用约定以及extern “C”的使用
- C/C++语言中的调用约定
- 解读C++调用约定
- C++ 函数调用约定和名称修饰
- C++调用约定及函数名修饰约定规则
- C++调用约定和名字约定
- __declspec(dllimport)和__declspec(dllexport)的区别,以及有关c/c++调用约定
- [转载]visual studio的C/C++修饰名及调用约定(如__cdecl)
- (转)__declspec(dllimport)和__declspec(dllexport)的区别,以及有关c/c++调用约定
- C++调用约定和名字约定
- [c++] 调用约定 calling convention
- C++调用约定和名字约定
- 调用约定 C++对照及汇编详解
- C++常用的调用约定__cdecl &__stdcall