数据结构系列文章(一):基础知识

引言:本系列文章的主要内容是重要数据结构的抽象数据类型和基本操作总结,包括编程语言的重要基础知识、线性表(顺序表、链表、数组、串、栈、队列)、树(二叉树、树与森林)、图(存储、遍历)。

基础知识1:程序的内存模型

程序执行时,可以将内存大致分为四个区域,分别是代码区、全局区、栈区(stack)、堆区(heap),不同区域存放的数据类型不同,且具有不同的生命周期。
在这里插入图片描述

程序执行前

在程序编译后、执行前,生成了exe可执行程序,未执行该程序前内存空间分为代码区和全局区。

代码区:存放函数体的二进制代码,由操作系统进行管理

1、代码区存放CPU执行的机器指令(已编译的代码转换成机器语言);

2、代码区是共享的,共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可,不会造成内存空间浪费;

3、代码区是只读的,使其只读的原因是防止程序意外地修改了它的指令。

全局区(静态区):存放全局变量、静态变量、常量(常量区)

1、全局区存放全局变量静态变量

​2、全局区还包含了常量区,存放字符串常量其他常量(即const修饰的变量),程序在运行的期间不能够被改变的量;

​3、全局区的数据在程序结束后由操作系统释放。例如静态变量,一旦静态区的内存被分配, 静态区的内存直到程序全部结束之后才会被释放。

示例程序:全局变量、全局常量、局部变量、局部常量、静态变量、字符串常量的内存位置

#include<iostream>
using namespace std;
// 全局变量
int global_a = 10;
int global_b = 15;
// const修饰的全局变量(全局常量)
const int gconst_a = 20;
const int gconst_b = 25;
int main()
{   
    // 局部变量
    int a = 30;
    int b = 35;
    // static修饰的静态变量
    static int static_a = 40;
    static int static_b = 45;
    // const修饰的局部变量(局部常量)
    const int lconst_a = 50;
    const int lconst_b = 55;
    cout << "全局变量global_a的地址为" << (int)&global_a << endl;
    cout << "全局变量global_b的地址为" << (int)&global_b << endl;
    cout << "全局常量gconst_a的地址为" << (int)&gconst_a << endl;
    cout << "全局常量gconst_b的地址为" << (int)&gconst_b << endl;
    cout << "静态变量static_a的地址为" << (int)&static_a << endl;
    cout << "静态变量static_b的地址为" << (int)&static_b << endl;
    cout << "字符串常量hello world的地址为" << (int)&("hello world") << endl;
    cout << "字符串常量hello world!的地址为" << (int)&("hello world!") << endl;
    cout << "局部变量a的地址为" << (int)&a << endl;
    cout << "局部变量b的地址为" << (int)&b << endl;
    cout << "局部常量lconst_a的地址为" << (int)&lconst_a << endl;
    cout << "局部常量lconst_b的地址为" << (int)&lconst_b << endl;
    return 0;
}

说明:只要写在函数体内的变量都是局部变量,包括main()函数。

转换成十进制地址格式如下:

全局变量global_a的地址为4210692
全局变量global_b的地址为4210696
全局常量gconst_a的地址为4214888
全局常量gconst_b的地址为4214892
静态变量static_a的地址为4210700
静态变量static_b的地址为4210704
字符串常量hello world的地址为4215046
字符串常量hello world!的地址为4215088
局部变量a的地址为6422284
局部变量b的地址为6422280
局部常量lconst_a的地址为6422276
局部常量lconst_b的地址为6422272

从上述的地址格式可以看出,全局变量、静态变量在一个区域附近(全局区),全局常量和字符串常量在一个区域附近(常量区),局部变量和局部常量在一个区域附近(堆区)。

常量或变量类型内存区域
全局变量全局区
static修饰的静态变量全局区
const修饰的全局变量(全局常量)常量区(也在全局区内)
字符串常量常量区(也在全局区内)
局部变量栈区
const修饰的局部变量(局部常量)栈区

程序执行后

栈区:存放函数的参数值、局部变量,由编译器自动分配和释放

1、由编译器自动分配释放,存放函数形参局部变量函数返回值等;

2、不要返回局部变量的地址,栈区开辟的数据由编译器自动释放

3、栈区中的数据作用域过了之后,系统就会自动管理并回收栈区的内存(分配内存,回收内存),不需要程序员手动管理;

4、栈区就像是一家客栈,里面有很多房间,客人来了之后自动分配房间,房间里的客人可以变动,是一种动态的数据变动

以下述测试程序为例,局部变量在栈区存放,栈区的数据在函数执行完以后就会自动释放,所以不要返回局部变量的地址。解决该问题的办法之一就是将数据开辟在堆区进行存储。

#include<iostream>
using namespace std;
int* fun()  //int类型的指针才可以返回int类型的地址
{
    int p = 20;     
    return &p;  //返回局部变量的地址
}
int main()
{   
    int *a = fun();
    cout << *a << endl;
    cout << *a << endl;
    return 0;
}

在这里插入图片描述

堆区:由程序员分配和释放,若程序员不释放,则程序结束时由操作系统回收

1、由程序员分配释放,若程序员不释放,程序结束时由操作系统回收

​2、程序员需要调用函数主动申请和释放内存空间:
在C语言中,主要利用malloc/free在堆区开辟/释放内存;
在C++中,主要利用new/delete在堆区开辟/释放内存;

3、若申请了堆区内存,之后忘记释放内存,很容易造成内存泄漏

#include<iostream>
using namespace std;
int* fun()  //int类型的指针才可以返回int类型的地址
{
    int *p = new int(10);     //new返回的是地址编号,需要用指针来接受这块内存
    return p;  //返回局部变量的地址
}
int main()
{   
    int *a = fun();
    cout<<*a<<endl;
    return 0;
}

栈区和堆区作用对比

1、在栈区上创建数组时,不能使用含有变量的表达式。例如,

int a[x+2];

是错误的!

原因:在栈区上创建数组时,编译器编译的时候就需要在栈区上分配内存。可是有了变量以后,编译器就无法知道该分配多大的内存空间,故编译器会报错。但是定义一般变量,例如:

int a;

编译器会自动识别 int 占多大内存,并分配给它。

2、在堆区上创建数组时,也就是创建动态数组时,就可以出现变量,例如

new a [x+1];

是正确的!

原因:在堆区上创建数组时,编译器不会在编译的时候为它分配内存,而是在程序运行的时候为它分配内存。我们可以知道,程序运行时变量的值就会明确是多少,故动态创建数组时可以出现变量。

总结:堆里的是动态数组,是程序运行过程中动态加载的,而栈不一样,申请数组必须要是确定大小的的数字,在编译时就要确定下来。

基础知识2:动态数组

实际编程中,所需的内存空间往往取决于实际输入的数据,并非总是可以预先确定的,这样用静态数组很难解决。为了解决上述问题,C/C++提供了一些内存管理函数,这些内存管理函数结合指针可以按需要动态地分配内存空间,来构建动态数组,也可把不再使用的空间回收待用,为有效地利用内存资源提供了手段。

动态数组,是相对于静态数组而言。静态数组的长度是预先定义好的,在整个程序中,一旦给定大小后就无法改变。而动态数组则不然,它可以随程序需要而重新指定大小。动态数组的内存空间是从堆区(heap)上分配(即动态分配)的。是通过执行代码而为其分配存储空间。当程序执行到这些语句时,才为其分配。程序员自己负责释放内存。

对于静态数组,其创建非常方便,使用完也无需释放,要引用也简单,但是创建后无法改变其大小是其致命弱点!

对于动态数组,其创建麻烦,使用完必须由程序员自己释放,否则严重会引起内存泄露。但其使用非常灵活,能根据程序需要动态分配大小。

基础知识3:有关指针的一些问题

什么是指针

空指针与野指针

指针的应用:利用指针访问数组中的元素

在这里插入代码片
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值