在计算机领域,堆栈是一个不容忽视的概念,堆栈是两种数据结构。
堆栈都是一种数据项按序排列的数据结构,只能在一端(称为栈顶(top))对数据项进行插入和删除。
要点:
堆,列队优先,先进先出。
栈,先进后出(First-In/Last-Out)。
栈(操作系统): 在函数调用时,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。全局变量和静态变量的存储是放在一块的,都放在全局区(静态区),初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放空间。
当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向函数的返回地址,也就是主函数中的下一条指令的地址,程序由该点继续运行。
堆(操作系统): 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于链表。堆则是存放在二级缓存中,生命周期由虚拟机的垃圾回收算法来决定(并不是一旦成为孤儿对象就能被回收)。所以调用这些对象的速度要相对来得低一些
堆(数据结构):堆可以被看成是一棵树,如:堆排序
栈(数据结构):一种先进后出的数据结构
栈的优势是,存取速度比堆要快,仅次于直接位于CPU中的寄存器。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。另外,栈数据可以共享,详见第3点。堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器.但缺点是,由于要在运行时动态分配内存,存取速度较慢。
堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容由程序员安排。
另外,栈有一个很重要的特殊性,就是存在栈中的数据可以共享。假设我们同时定义:
int a=3;
int b=3;
编译器先处理int a= 3;首先它会在栈中创建一个变量为a的引用,然后查找有没有字面值为3的地址,没找到,就开辟一个存放3这个字面值的地址,然后将a指向3的地址。接着处理intb= 3;在创建完b的引用变量后,由于在栈中已经有3这个字面值,便将b直接指向3的地址。这样,就出现了a与b同时均指向3的情况。
特别注意的是,这种字面值的引用与类对象的引用不同。假定两个类对象的引用同时指向一个对象,如果一个对象引用变量修改了这个对象的内部状态,那么另一个对象引用变量也即刻反映出这个变化。相反,通过字面值的引用来修改其值,不会导致另一个指向此字面值的引用的值也跟着改变的情况。如上例,我们定义完a与b的值后,再令a=4;那么,b不会等于4,还是等于3。在编译器内部,遇到a=4;时,它就会重新搜索栈中是否有4的字面值,如果没有,重新开辟地址存放4的值;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。
这里有必要对变量的存储方式进行说明:
在C++中,内存分成5个区,他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区
1.栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清楚的变量的存储区。里面的变量通常是局部变量、函数参数等。
2.堆,就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete.如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。
3.自由存储区,就是那些由malloc等分配的内存块,他和堆是十分相似的,不过它是用free来结束自己的生命的。
4.全局/静态存储区,全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区。
5.常量存储区,这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改(当然,你要通过非正当手段也可以修改)
变量存储特性:
存储描述 | 持续性 | 作用域 | 链接性 | 如何声明 |
自动 | 自动 | 代码块 | 无 | 在代码块中 |
寄存器 | 自动 | 代码块 | 无 | 在代码块中,使用关键字 register |
静态,无链接性 | 静态 | 代码块 | 无 | 在代码快中,使用关键字 static |
静态,外部链接性 | 静态 | 文件 | 外部 | 不在任何函数内 |
静态,内部链接性 | 静态 | 文件 | 内部 | 不在任何函数内,使用关键字 static |
首先,定义静态变量时如果没有初始化编译器会自动初始化为0.。接下来,如果是使用常量表达式初始化了变量,则编译器仅根据文件内容(包括被包含的头文件)就可以计算表达式,编译器将执行常量表达式初始化。必要时,编译器将执行简单计算。如果没有足够的信息,变量将被动态初始化。
所有的静态持续变量都有下述初始化特征: 未被初始化的静态变量的所有位都被设为0。这种变量被称为零初始化。以上代码说明关键字static的两种用法,但含义有些不同:用于局部声明,以指出变量是无链接性的静态变量时,static表示的是存储持续性;而用于代码块外声明时,static表示内部链接性,而变量已经是静态持续性了。有人称之为关键字重载,即关键字的含义取决于上下文。
请看一下代码:
#include<iostream>
using namespace std;
int global_1=1000; //静态变量外部链接性,存放在静态区
int global_2; //静态变量外部链接性,存放在静态区
static intone_file_1=1000; //静态变量内部链接性,存放在静态区
static intone_file_2; //静态变量内部链接性,存放在静态区
int func(int a,int b);
int main()
{
static int count_1=1000;//静态变量无链接性,存放在静态区
int t; //普通变量,存放在栈
char s[] = "abc"; //普通变量,存放在栈
char *p2; //普通变量,存放在栈
char *p3 = "123456"; //123456\0在常量区,p3在栈上。
cr=func(t,p); //函数返回值,实参等都存放在栈
p1 = (char *)malloc(10); //动态分配的内存空间,存放在堆
p2 = (char *)malloc(20); //动态分配的内存空间,存放在堆
return 0;
}