压栈是什么意思

压栈是什么意思,第1张

在计算机系统中,栈则是一个具有以上属性的动态内存区域。

程序可以将数据压入栈中,也可以将数据从栈顶d出,在i386机器中,栈顶由称为esp的寄存器进行定位。压栈的 *** 作使得栈顶的地址减小,d出的 *** 作使得栈顶的地址增大。

栈在程序的运行中有着举足轻重的作用,最重要的是栈保存了一个函数调用时所需要的维护信息,这常常称之为堆栈帧或者活动记录,一般包含如下几方面的信息:

1.函数的返回地址和参数

2. 临时变量:包括函数的非静态局部变量以及编译器自动生成的其他临时变量。

这个值压入栈也是程序开发中常见的一种方法,

从给定活动记录或从一个函数中获取一个局部变量的信息。

对于第一种情况, 参数 ar 必须是一个有效的活动的记录。 这条记录可以是前一次调用 lua_getstack 得到的, 或是一个钩子 的参数。 索引 n 用于选择要检阅哪个局部变量; 参见 debug.getlocal 中关于变量的索引和名字的介绍。

lua_getlocal 将变量的值压栈,并返回其名字。

对于第二种情况,ar 必须填 NULL 。 需要探知的函数必须放在栈顶。 对于这种情况,只有 函数的形参是可见的 (没有关于还有哪些活动变量的信息) 也不会有任何值压栈。

当索引大于活动的局部变量的数量, 返回 NULL (无任何压栈)

从给定活动记录或从一个函数中获取一个局部变量的信息。

对于第一种情况, 参数 ar 必须是一个有效的活动的记录。 这条记录可以是前一次调用 lua_getstack 得到的, 或是一个钩子 的参数。 索引 n 用于选择要检阅哪个局部变量; 参见 debug.getlocal 中关于变量的索引和名字的介绍。

lua_getlocal 将变量的值压栈,并返回其名字。

对于第二种情况,ar 必须填 NULL 。 需要探知的函数必须放在栈顶。 对于这种情况,只有 函数的形参是可见的 (没有关于还有哪些活动变量的信息) 也不会有任何值压栈。

当索引大于活动的局部变量的数量, 返回 NULL (无任何压栈)

从给定活动记录或从一个函数中获取一个局部变量的信息。

对于第一种情况, 参数 ar 必须是一个有效的活动的记录。 这条记录可以是前一次调用 lua_getstack 得到的, 或是一个钩子 的参数。 索引 n 用于选择要检阅哪个局部变量; 参见 debug.getlocal 中关于变量的索引和名字的介绍。

lua_getlocal 将变量的值压栈,并返回其名字。

对于第二种情况,ar 必须填 NULL 。 需要探知的函数必须放在栈顶。 对于这种情况,只有 函数的形参是可见的 (没有关于还有哪些活动变量的信息) 也不会有任何值压栈。

当索引大于活动的局部变量的数量, 返回 NULL (无任何压栈)

从给定活动记录或从一个函数中获取一个局部变量的信息。

对于第一种情况, 参数 ar 必须是一个有效的活动的记录。 这条记录可以是前一次调用 lua_getstack 得到的, 或是一个钩子 的参数。 索引 n 用于选择要检阅哪个局部变量; 参见 debug.getlocal 中关于变量的索引和名字的介绍。

lua_getlocal 将变量的值压栈,并返回其名字。

对于第二种情况,ar 必须填 NULL 。 需要探知的函数必须放在栈顶。 对于这种情况,只有 函数的形参是可见的 (没有关于还有哪些活动变量的信息) 也不会有任何值压栈。

当索引大于活动的局部变量的数量, 返回 NULL (无任何压栈)

什么是堆和栈?

一个由c/C++编译的程序占用的内存分为以下几个部分

1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其 *** 作方式类似于数据结构中的栈。

2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。

3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后有系统释放

4、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放

5、程序代码区—存放函数体的二进制代码。

函数压栈是怎么回事?

函数压栈的本质是参数传递

这又跟汇编语言连系起来了.汇编语言的过程即proc可以理解成函数

比如一个最简单的计算两数之和函数

如果用汇编来写估计是这样的

sub proc

pop ax 从stack取a 并放在AX寄存器中

pop bx 从stack取b 并放在BX寄存器中

add ax,bx 计算a+b

ret //返回

sub endp

显然要调用这个函数,你应当先把b值push进stack,然后再push a

因为stack是先进后出的

所以调用汇编像这样

比如计算4+5

push 5

push 4

call sub//返回值在AX中

在这个例子中先压5或先压4得到的结果没有变化

但大多数程序,如果参数的顺序错误将是灾难性的

因为不管什么高级语言最终都要编译成汇编语言,然后是机器语言

同样下面这个C程序,计算a+b值,必然会编译成上面的汇编代码

int sub(int a ,int b) {return a+b}

所以C在调用这个函数sub时,必须要压栈(即传入参数)但这些工作,在C语言里,并不需要你来完成.你只要写出

sub(7,9)

编译器在编译成汇编时就会自动完成相关的压栈工作.

根据函数调用方式和参数压入顺序目前存在三种约定:

stdcall

cdecl

fastcall

这都相关压栈顺序和栈的清理工作约定

他们的细节都不相同,但有一点是肯定的,参数比须从右向左压入栈中

stdcall中 函数必须自已清理栈

cdecall 由调用者清除堆栈 C的默认函数调用方式 所以这样C支持可变参数

fastcall 是把函数参数列表的前三个参数放入寄存器eax,edx,ecx,其他参数压栈

源代码:

int function(int a, int b)

{

return a + b

}

void main()

{

function(10, 20)

}

1.__cdecl

_function

push ebp

mov ebp, esp

mov eax, [ebp+8]参数1

add eax, [ebp+C]加上参数2

pop ebp

retn

_main

push ebp

mov ebp, esp

push 14h 参数 2入栈

push 0Ah 参数 1入栈

call _function 调用函数

add esp, 8 修正栈

xor eax, eax

pop ebp

retn

2.__fastcall

@function@8

push ebp

mov ebp, esp保存栈指针

sub esp, 8 多了两个局部变量

mov [ebp-8], edx保存参数 2

mov [ebp-4], ecx保存参数 1

mov eax, [ebp-4]参数 1

add eax, [ebp-8]加上参数 2

mov esp, ebp修正栈

pop ebp

retn

_main

push ebp

mov ebp, esp

mov edx, 14h参数 2给EDX

mov ecx, 0Ah参数 1给ECX

call @function@8 调用函数

xor eax, eax

pop ebp

retn

3.__stdcall

_function@8

push ebp

mov ebp, esp

mov eax, [ebp] 参数 1

add eax, [ebp+C]加上参数 2

pop ebp

retn 8 修复栈

_main

push ebp

mov ebp, esp

push 14h 参数 2入栈

push 0Ah 参数 1入栈

call _function@8函数调用

xor eax, eax

pop ebp

retn


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/11851129.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-19
下一篇 2023-05-19

发表评论

登录后才能评论

评论列表(0条)

保存