函数调用栈空间的分配和释放
2015-04-07 18:48
1451 查看
1 函数执行的时候有自己的临时栈 (c++中函数调用时有两个栈空间,对象的栈空间和函数的栈空间)
2 函数的参数就在临时栈中,如果函数传递实参过去,则用来初始化临时参数变量。
参数是fun传递的,实际调用的函数是add,在调用函数初始化临时变量时,如果栈中有传参数,则初始化临时变量,若没有,则不初始化。
所以上文中会出现一个很大的值,因为b没有被初始化
但如果
int (*fun)(int)
= (int(*)(int))add; 改成 int (*fun)(int,int,int) = (int(*)(int,int,int ))add;
int r
= fun(1); 改成 int r = fun(1,2,5);
这时的返回值是正确的(3),因为add中的2个临时变量都被初始化来,并且add中没有第三个参数,所以5不会被使用.也不会出问题
3 返回值的返回形式
通过寄存器返回值 (通过返回值返回值)
通过参数返回值 (存放返回值的参数必须是指针,指针指向的区域必须事先分配)
如果通过参数返回指针,那么参数就必须是双指针 (以此类推,参数是三指针,是向通过参数返回双指针)
4 调用约定
__stdcall
__cdecl
__fastcall
调用约定(Calling convention)决定以下内容: 函数参数的压栈顺序,由调用者还是被调用者把参数弹出栈,以及产生函数修饰名的方法。
采用__cdecl约定时:函数参数按照从右到左的顺序入栈,并且由调用函数者把参数弹出栈以清理堆栈。
采用__stdcal约定时:函数参数按照从右到左的顺序入栈,被调用的函数在返回前清理传送参数的栈,函数参数个数固定。由于函数体本身知道传进来的参数个数,因此被调用的函数可以在返回前用一条ret n指令直接清理传递参数的堆栈。
采用__fastcall约定时:将函数的从左边开始的两个大小不大于4个字节(DWORD)的参数分别放在ECX和EDX寄存器,其余的参数仍旧自右向左压栈传送,被调用的函数在返回前清理传送参数的堆栈。__fastcall约定一般指传送不超过4个字节的参数,通过寄存器,不用栈,这样比较快。
__cdecl和__stdcal之间的唯一区别在于返回时是由被调用者清理栈,还是由调用用者清理栈。
但是,这两种清理栈的方式,会对有什么影响呢?
__stdcall与__cdecl两者之间的区别:
WINDOWS的函数调用时需要用到栈(STACK,一种先入后出的存储结构)。当函数调用完成后,栈需要清除,这里就是问题的关键,如何清除?
如果函数使用_cdecl,那么栈的清除工作是由调用者,用COM的术语来讲就是客户来完成的。这样带来了一个棘手的问题,不同的编译器产生栈的方式不尽相同,那么调用者能否正常的完成清除工作呢?答案是不能。
如果使用__stdcall,上面的问题就解决了,函数自己解决清除工作。所以,在跨(开发)平台的调用中,我们都使用__stdcall(虽然有时是以WINAPI的样子出现)。
那么为什么还需要_cdecl呢?当我们遇到这样的函数如fprintf()它的参数是可变的,不定长的,被调用者事先无法知道参数的长度,事后的清除工作也无法正常的进行,因此,这种情况我们只能使用_cdecl。
到这里我们有一个结论,如果你的程序中没有涉及可变参数,最好使用__stdcall关键字。
语法格式
linux下
int __attribute__((stdcall)) add(int *a,int *b){}
windows下
int __stdcall add(int *a,int *b) {}
来源:http://m.blog.csdn.net/blog/wa8887396/8964712
2 函数的参数就在临时栈中,如果函数传递实参过去,则用来初始化临时参数变量。
#include <stdio.h> int add(int a,int b) { printf("%d,%d\n",a,b); return a+b; } int main() { int (*fun)(int) = (int(*)(int))add; int r = fun(1); printf("%d\n",r); return 0; }
参数是fun传递的,实际调用的函数是add,在调用函数初始化临时变量时,如果栈中有传参数,则初始化临时变量,若没有,则不初始化。
所以上文中会出现一个很大的值,因为b没有被初始化
但如果
int (*fun)(int)
= (int(*)(int))add; 改成 int (*fun)(int,int,int) = (int(*)(int,int,int ))add;
int r
= fun(1); 改成 int r = fun(1,2,5);
这时的返回值是正确的(3),因为add中的2个临时变量都被初始化来,并且add中没有第三个参数,所以5不会被使用.也不会出问题
3 返回值的返回形式
通过寄存器返回值 (通过返回值返回值)
通过参数返回值 (存放返回值的参数必须是指针,指针指向的区域必须事先分配)
如果通过参数返回指针,那么参数就必须是双指针 (以此类推,参数是三指针,是向通过参数返回双指针)
4 调用约定
__stdcall
__cdecl
__fastcall
调用约定(Calling convention)决定以下内容: 函数参数的压栈顺序,由调用者还是被调用者把参数弹出栈,以及产生函数修饰名的方法。
采用__cdecl约定时:函数参数按照从右到左的顺序入栈,并且由调用函数者把参数弹出栈以清理堆栈。
采用__stdcal约定时:函数参数按照从右到左的顺序入栈,被调用的函数在返回前清理传送参数的栈,函数参数个数固定。由于函数体本身知道传进来的参数个数,因此被调用的函数可以在返回前用一条ret n指令直接清理传递参数的堆栈。
采用__fastcall约定时:将函数的从左边开始的两个大小不大于4个字节(DWORD)的参数分别放在ECX和EDX寄存器,其余的参数仍旧自右向左压栈传送,被调用的函数在返回前清理传送参数的堆栈。__fastcall约定一般指传送不超过4个字节的参数,通过寄存器,不用栈,这样比较快。
__cdecl和__stdcal之间的唯一区别在于返回时是由被调用者清理栈,还是由调用用者清理栈。
但是,这两种清理栈的方式,会对有什么影响呢?
__stdcall与__cdecl两者之间的区别:
WINDOWS的函数调用时需要用到栈(STACK,一种先入后出的存储结构)。当函数调用完成后,栈需要清除,这里就是问题的关键,如何清除?
如果函数使用_cdecl,那么栈的清除工作是由调用者,用COM的术语来讲就是客户来完成的。这样带来了一个棘手的问题,不同的编译器产生栈的方式不尽相同,那么调用者能否正常的完成清除工作呢?答案是不能。
如果使用__stdcall,上面的问题就解决了,函数自己解决清除工作。所以,在跨(开发)平台的调用中,我们都使用__stdcall(虽然有时是以WINAPI的样子出现)。
那么为什么还需要_cdecl呢?当我们遇到这样的函数如fprintf()它的参数是可变的,不定长的,被调用者事先无法知道参数的长度,事后的清除工作也无法正常的进行,因此,这种情况我们只能使用_cdecl。
到这里我们有一个结论,如果你的程序中没有涉及可变参数,最好使用__stdcall关键字。
语法格式
linux下
int __attribute__((stdcall)) add(int *a,int *b){}
windows下
int __stdcall add(int *a,int *b) {}
来源:http://m.blog.csdn.net/blog/wa8887396/8964712
相关文章推荐
- 高级编程学习笔记(二) 函数调用栈空间的分配和释放
- Linux学习笔记4——函数调用栈空间的分配与释放
- C语言free()函数:释放动态分配的内存空间
- // 在利用子函数实现内存堆分配的实现时出现的问题,同时也需要利用子函数实现内存堆的释放。
- C++_动态存储空间的分配和释放
- Linux中的内存分配和释放之mem_init()函数分析
- Linux中的内存分配和释放之__free_pages()函数分析
- Linux中的内存分配和释放之__alloc_boot函数分析
- 使用CRT调试内存分配堆来找出未释放的内存空间
- 使用指针类型参数和使用指针引用类型参数在函数内部用new给参数分配空间的不同结果
- 函数域中申请堆空间出函数后不会自动释放
- new分配空间之后和delete释放空间之后
- delete 基类指针 释放为派生类分配的空间
- 二维数组分配,释放空间
- 总结一下函数开始分配资源怎么在多条件返回时释放资源
- 使用CRT调试内存分配堆来找出未释放的内存空间
- Linux中的内存分配和释放之bootmam_init()函数分析
- [转]OpenCV: 分配与释放图像空间
- Linux中的内存分配和释放之kmem_cache_create()函数分析
- 【问题】c/c++函数内部动态分配的内存,函数执行完毕会释放吗?