堆与栈是两种数据结构,并不是一种数据结构,堆是堆,栈是栈。
1、栈:是一种只能在一端进行插入和删除的数据结构。
允许插入与删除的一端被称为栈顶,另一端被称为栈底。
按照先进后出的顺序存储数据。最先进入的数据存放在栈底,最后进入的数据存放在栈顶。
当栈里的元素为空时,称为空栈。
在函数调用的时候用于存储断点,递归的时候,也需要用到栈。
栈一般包含以下两个方面的信息:
(1)函数的返回地址和参数。
(2)临时变量:包括函数的非静态局部变量及编译器自动生成的其他临时变量。
在汇编语言中,栈顶由esp寄存器进行定位,压栈的操作使得栈顶的地址减小,弹出的操作使得栈顶的地址增大。
2、堆:是一种动态的存储结构,实际上是数据段中的自由存储区,常用语存储、分配动态数据。
堆中存入的数据地址向增加方向变动。
堆可以不断的进行分配直到没有堆空间为止,也可以随时进行释放、在分配,不存在顺序问题。
在C语言中,堆空间的分配通常通过malloc()、calloc()、realloc()三个函数来实现的,而堆的释放是通过free()函数来进行释放分配的空间。
3、栈的体验
如何通过栈将数组里面的数据通过print函数打印出来呢?
print函数没有参数,应该在print函数里面填充什么代码,可以实现将arr数组里面的数据全部打印出来呢?
#include void print(){}int main(){ int a = 1; int b = 2; char c = 'c'; int arr[] = { 11,12,13,14,15,16,17 }; print(); return 0;}
那么这就涉及到汇编的一些东西了,具体汇编如下:
1、push操作先移动栈顶指针,之后将信息入栈。
2、eps为栈顶指针,压栈的操作使得栈顶的地址减小,弹出的操作使得栈顶的地址增大。
3、ebp是32位的bp,是基址指针。bp为基指针寄存器,用它可以直接存取堆栈中的数据,它在调用函数时保存esp,以便函数结束时可以正确返回。
4、默认的函数内部变量的压栈操作为:从上到下,从左到右,采用4字节对齐,数组压栈方法略有不同,即从最后一个元素开始,直到起始元素为止,即采用从右到左的方法压栈。
在main()函数和print()函数的开头都有如下两句汇编指定:
第一个push指针的操作步骤是,首先移动栈顶指针esp,然后将ebp内容压栈,注意此时压栈的ebp的值为上一个函数的esp的值,而esp恰好就是上一个函数的栈底。
所以每个函数一开始push指令就是保存上一个函数的栈底。
由于esp是当前的栈顶指针,所以该指针的作用就是保存当前栈顶指针的值。
由此就可以得出,ebp存放的就是此刻栈顶的地址,也就是说,ebp是一个指针,指向栈顶,而栈顶存放的数据就是上一个函数的ebp的值,就上一个函数的栈底。
所以我们只需要在print函数中得到main函数的栈底,就可以去取出数组中的每一个元素了。
具体实现方法如下:
void print(){ unsigned int _ebp; __asm { mov _ebp, ebp; } int *p = (int *)(*(int *)_ebp - 4 - 4 - 4 - 4 * 7 ); for (int i = 0; i < 7; i++) { printf("%d