【IT168 技术文档】当您开始使用动态链接库,编写涉及其他语言中的函数代码时,你会遇到诸如stdcall,safecall,cdecl和winapi之类的词。这些调用约定定义了如何在运行时调用外部函数,即使是单独编译,甚至可能是和不同的编译器和连接器。
本文是一篇关于调用约定的小教程,旨在解释调用约定如何运作,并且与C和C + +中书写动态链接库以及在C#利用他们相关联。这可能对理解骨架代码有用。
备注:这无关链接,而是使用动态数据库并在运行时间调用它们。
一个调用约定定义了参数如何被传到堆栈,调用者或函数是否需要在结束调用后清理堆栈。
关于堆栈
堆栈追踪函数的调用来源。而这通常是硬件通过使用注册簿来完成的,注册簿是对记忆的指向标。按照惯例,一个堆栈开始是指向内存。随着每个项目都堆到堆栈上,堆栈指针就会减少。当你从堆栈上删除数据时。
一般来说,编译器具备一些设置可以指定堆栈可得的内存数量。堆栈可以保存以下三种类型的数据:
- 返回地址;
- 函数参数;
- 本地变量。
CPU会有一些针对堆栈记忆分配的特殊指令。编译器会计算出所有本地变量和参数所需要的内存量,并且相应地对其进行内存分配。在调用快结束时,准确的反面指令被调用来减少同样的量。基本上,这些指令通过登记的数额减少了堆栈指针,并且在最后又相应地增加了空间。
返回地址
当一个函数被调用的时候,CPU要做的第一件事是在调用的函数生成时,计算出下一个指令是什么。下表就是对该过程进行的一个小小的演示:
Address 101
Address 102 Call Function 201
Address 103...
...
Address 151 Call Function 201
Address 152...
Address 201.. First instruction of function
Address 202.. Allocate memory for local variables
...
Address 206.. Release the allocated memory
Address 207.. return from function
在上面的例子中,如果CPU刚刚在101处理完指令,那么其下一步操作就是处理102的指令并且调用201的函数。那之后的指令就是103。它会一直持续到151直到再次调用201的函数。这次,当它返回时,它会一直与152的指令持续。地址103和152是保存在堆栈的返回地址。
堆栈状态
如果堆栈以5000的指标开始,它就会像这样:
Stack Pointer= 5000
Address 5000
当函数201在202指令运行完后被调用,堆栈就会有数据。让我们想象一下这个函数没有参数但是有一个拥有本地变量总数,该总数是由10个整数组成的数组。
int totals[10]
如果每个地址保存一个整数,那么这个数组就有十个地点。在201,堆栈指数会减少10。
Stack Pointer= 4989
Address 5000 .. 103
Address 4999 .. total[9]
Address 4998 .. total[8]
...
Address 4990 .. total[0]
Address 4989 <- Current top of stack.
执行206时,堆栈指针将增加10个。207的指令会弹到堆栈的极限。这一情况正是一种警告。与推送一起,某个值也被存储在当前地址中,该地址由堆栈指针持有,然后堆栈指针递减。膨胀使堆栈指针增加了1,然后从堆栈指针所持有的地址处得到值。因此,从5000(地址)得到值,即103 ,而这就是下一个要执行的指令。
由数值审核还是由参考审核?
前者把整个的变量复制到堆栈上,这不但很慢而且如果变量很大,可能使堆栈无法承担。例如,10,000个数组会减少10,000个堆栈指数并且把这10,000个整数的值到复制到堆栈上。相比之下,使用参考审核则要快得多,因为它只会把变量的地址送到堆栈上。所以在C语言中是用指数(*),在c++中使用参考(&)来保持其简便。