在我们通常理解的Stack 里,存在有不同的含义。简单的解释一下。
最常见的栈数据结构
最常见的含义就是栈,一种数组形式的储存形式,后进先出的特点,针对这样的数据结构,配套的函数应当:
- push 顶层加入元素
- pop 返回顶层元素,并弹出
- top 返回顶层元素,不弹出
- isempty 返回当前栈是否为空。
代码存放的区域
stack 的另一种含义是存放数据的一种内存区域,参考维基百科 ,我们知道,程序包含几个区域,静态区,堆区,栈区。
- 栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
- 堆区(heap) — 一般由程序员分配释放 , 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。
- 全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域(BSS)。 - 程序结束后由系统释放
- 文字常量区 — 常量字符串就是放在这里的。 程序结束后由系统释放
- 程序代码区— 存放函数体的二进制代码。
下面这是个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| main.cpp int a = 0; 全局初始化区 char *p1; 全局未初始化区 main() { int b; 栈 char s[] = "abc"; 栈 char *p2; 栈 char *p3 = "123456"; 123456在常量区,p3在栈上。 static int c =0; 全局(静态)初始化区 p1 = (char *)malloc(10); p2 = (char *)malloc(20); 分配得来得10和20字节的区域就在堆区。 strcpy(p1, "123456"); 123456放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。 }
|
对于stack 来说,stack 是有结构的,每个区块按照一定次序存放,可以明确知道每个区块的大小;但是heap 是没有结构的,数据可以任意的存放,因此,stack 的寻址速度要快于heap。
而且,一般来说,对每个线程会分配一个stack ,每个进程分配一个heap 。那么stack 就是线程独占的,而heap 则是线程共用的。stack 在创建的时候,大小是确定的,数据超过这个大小,就发生stack overflow 错误,而heap 的大小是不确定的,需要的话,可以不断的增加。
所以,只要是局部的,占用空间确定的数据,都会存放在stack 里边,其他的不确定的,动态生成的,就存放在heap 里去。
比如我们的创建新的对象,就会存放在heap中,因为stack 在使用完后会又编译器自动释放,但是heap 如果不去有效的清理,等待系统的垃圾清理机制去将这块内存清理,就很容易发生内存溢出。
更多详细的解释,可以参考这篇文章
代码运行的方式- 调用栈
第三种含义可以是调用栈,call stack 。它用的就是栈的思路,表示函数或者子例程像堆积木一样存放,以实现层层调用。
对于调用栈的方式,我们举个例子,该例子来自java 8 stack and heap
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| class Student{ int age; String name; public Student(int Age, String Name) { this.age = Age; setName(Name); } public void setName(String Name) { this.name = Name; } } public class Main{ public static void main(String[] args) { Student s; s = new Student(23,"Jonh"); } }
|
其调用main 方法时候,需要生成一个student实例,于是调用student 构造函数,在构造函数中又调用了setName方法。如图(来自阮一峰):
script>