数据结构以及内存中的堆和栈

数据结构的堆和栈

数据结构中,堆(heap)与栈(stack)是两个常见的数据结构,理解二者的定义、用法与区别,能够利用堆与栈解决很多实际问题。

1.1 栈简介
栈是一种运算受限的线性表,其限制是指只仅允许在表的一端进行插入和删除操作,这一端被称为栈顶(Top),相对地,把另一端称为栈底(Bottom)。把新元素放到栈顶元素的上面,使之成为新的栈顶元素称作进栈、入栈或压栈(Push);把栈顶元素删除,使其相邻的元素成为新的栈顶元素称作出栈或退栈(Pop)。这种受限的运算使栈拥有“先进后出”的特性(First In Last Out),简称FILO。

(1)栈的分类

栈分顺序栈和链式栈两种。栈是一种线性结构,所以可以使用数组或链表(单向链表、双向链表或循环链表)作为底层数据结构。使用数组实现的栈叫做顺序栈,使用链表实现的栈叫做链式栈,二者的区别是顺序栈中的元素地址连续,链式栈中的元素地址不连续。

栈的结构如下图所示:

1.2 堆简介

(1)堆的简介及性质
堆是一种常用的树形结构,是一种特殊的完全二叉树,当且仅当满足所有节点的值总是不大于或不小于其父节点的值的完全二叉树被称之为堆。堆的这一特性称之为堆序性。

Heap: 堆。一般也称作Priority Queue(即优先队列)

因此,在一个堆中,根节点是最大(或最小)节点。如果根节点最小,称之为小顶堆(或小根堆),如果根节点最大,称之为大顶堆(或大根堆)。堆的左右孩子没有大小的顺序。

堆的应用十分广泛,下面给出一些堆的常见操作:

上浮 shift_up;
下沉 shift_down
插入 push
弹出 pop
取顶 top
堆排序 heap_sort

内存中的堆区和栈区

一个由C/C++编译的程序占用的内存分为以下几个部分
1、栈区(stack):由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
2、堆区(heap):一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式类似于链表。new出来的放在这里。
3、数据段:(static)全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域。程序结束后由系统释放。
4、BSS段:未初始化的全局变量和未初始化的静态变量就是放在这里的。程序结束后由系统释放。
5、程序代码段:存放函数体的二进制代码。

总结:

在这里插入图片描述

在没有运行程序前,也就是说程序没有加载到内存前,可执行程序内部已经分好3段信息,分别为代码段(text)、数据段(data)和未初始化数据段(bss)3 个部分

代码段
存放 CPU 执行的机器指令。通常代码区是可共享的(即另外的执行程序可以调用它),使其可共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可。代码区通常是只读的,使其只读的原因是防止程序意外地修改了它的指令。另外,代码区还规划了局部变量的相关信息。

全局初始化数据区/静态数据区(数据段)
该区包含了在程序中明确被初始化的全局变量、已经初始化的静态变量(包括全局静态变量和局部静态变量)和常量数据(如字符串常量)。

未初始化数据区(又叫 bss 段)
存入的是全局未初始化变量和未初始化静态变量。未初始化数据区的数据在程序开始执行之前被内核初始化为 0 或者空(NULL)。

程序在加载到内存前,代码区和全局区(data和bss)的大小就是固定的,程序运行期间不能改变。然后,运行可执行程序,系统把程序加载到内存,除了根据可执行程序的信息分出代码区(text)、数据区(data)和未初始化数据区(bss)之外,还额外增加了栈区、堆区。

栈区(stack)
栈是一种先进后出的内存结构,由编译器自动分配释放,存放函数的参数值、返回值、局部变量等。在程序运行过程中实时加载和释放,因此,局部变量的生存周期为申请到释放该段栈空间。

堆区(heap)
堆是一个大容器,它的容量要远远大于栈,但没有栈那样先进后出的顺序。用于动态内存分配。堆在内存中位于BSS区和栈区之间。一般由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收。

堆区和栈区区别

申请大小的限制
栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。

堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。

申请效率的比较:
栈由系统自动分配,速度较快。但程序员是无法控制的。

堆是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便.

另外,在WINDOWS下,最好的方式是用VirtualAlloc分配内存,他不是在堆,也不是在栈是直接在进程的地址空间中保留一快内存,虽然用起来最不方便。但是速度快,也最灵活。
堆和栈中的存储内容
栈: 在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。

当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。

堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。

存取效率的比较
char s1[] = “aaaaaaaaaaaaaaa”;
char *s2 = “bbbbbbbbbbbbbbbbb”;

aaaaaaaaaaa是在运行时刻赋值的;
而bbbbbbbbbbb是在编译时就确定的;
但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。

另一处介绍:

(1)、管理方式不同:栈直接由编译器管理(产生和消除),堆由程序员管理,程序员管理其的产生和消除
(2)、空间大小不同:栈占用的空间较小,而堆占用的空间较大
(3)、能否产生碎片不同:栈不会产生碎片,但是堆会产生,会有内存泄露的问题
(4)、生长方向不同:栈是向下压栈,堆是向上存放数据
(5)、分配方式不同:栈是在程序员申请之后,由系统分配的没有经过初始化的变量,只有动态分配方式。而堆是由程序员自己实例化,创建的已经过初始化的变量,分配方式类似于链表,动态分配和静态分配都可以
(6)、分配效率不同:栈是由内存分配的,系统专门为其准备寄存器,同时有专门的出栈和入栈指令,因而效率比较高。而堆空间则是C库分配的,可能会存在碎片的原因导致内存不连续,因而效率比较低

全局/局部/静态/动态 变量
全局变量在所有函数外定义,作用于全局域,在其他文件中使用的时候需要使用extern关键字声明。
局部变量具有局部作用域,在函数执行时存在,函数执行结束即注销。
静态局部变量,初始化一次,然后一直存在到程序结束,但是只对定义自己的函数体可见。
静态全局变量,作用于全局域,如果有多个文件,只能作用域一个文件,如果有两个源文件定义同一个名字的静态全局变量,不会发生冲突。

const
在这里插入图片描述
Const char*p //p 指向的内容不能被修改
Char const *p; // p指针指向内容不能修改

Const (char*) p; //p指针不能修改,p++ 操作会出错
Const type fun(); // 返回值类型为一个const type类型,不能修改
Fun( const char *p); //保护指针,引用传递的值不被修改.
类成员函数:中 fun() const; //表明FUN不能修改成员变量,不调用非const 成员函数.

const用法借鉴:
https://blog.csdn.net/magic_world_wow/article/details/80733495


版权声明:本文为weixin_44548640原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。