当前课程知识点:基于Linux的C++ >  第七讲 指针与引用 >  7.7 动态存储管理(一) >  LinuxCPP0707

返回《基于Linux的C++》慕课在线视频课程列表

LinuxCPP0707在线视频

LinuxCPP0707

下一节:LinuxCPP0708

返回《基于Linux的C++》慕课在线视频列表

LinuxCPP0707课程教案、知识点、字幕

接下来一节就是我们的动态存储管理

这个涉及到非常复杂的

动态内存分配的任务

标准库里边

C架构提供了内存分配函数malloc

和内存释放函数free

它还提供了C++的内存分配管理操作符

new和delete

new是内存分配 delete是删除

首先我们来看内存分配与释放

在我们的C++的程序代码中

内存分配或释放

实际上是有三种内存管理方式

一个是静态的内存分配

正常情况下

所有的全局数据对象和静态的局部量

都适用我们的静态内存分配的模式

在程序运行前分配好

程序结束了它就释放

也就是说你按照静态的方式分配这些内存

它的生存期是巨大的

它就和我们的程序一样得长

实际上 比我们真正的main函数运行还要长

它在main函数之前就能分完

main函数做完了它才能够销毁

第二种就是自动内存分配

它的使用对象就是普通的局部数据对象

什么时候分配呢

你的程序流程什么时候进入这个函数块

或者这个复合语句块

才为它分配内存 离开了它就销毁

第三种 今天要讲的动态内存分配

你想让它什么时候分配就什么时候分配

你想让它什么时候销毁就什么时候销毁

全由程序员编程说了算

编程 你要分配它 就调用malloc

或者使用new操作符

你要销毁它 就调用free

或者使用delete操作符

这两种方案都可以

一种是C的方案 一种是C++方案

那么我们为什么需要动态内存分配

静态的和自动的这个内存分配方式

必须事先了解这个数据对象的格式

和它的存储空间的大小

你不了解这一点

编译器没有办法替你分配

刚好合适的存储空间

但是有些时候 我们没有办法事先决定

这个数据对象的大小

静态分配和自动分配这个事情就做不了

你比如讲 我要你声明包含n个元素的

一个整数数组

n的值由用户在程序运行的时候输入

你怎么办呢 程序员你写这个程序

别人要用你的程序

然后他会分配一个n个元素的整数数组

n值什么时候来呢

他运行这个程序 输入一个元素个数

他就给你输出一个元素个数

然后你就分配那么多个元素

那你的程序在写的时候

你不知道用户在执行的时候

他会输入什么样的值啊

你分配两个元素 不够

三个怎么办 冒了

一冒了怎么办 程序就崩了

十个 够吗 不够

250够吗 不够

1万够吗 不够

多少它都不够

就是说当你碰到这种问题的时候

静态和自动两种分配方式

解决不了这个问题

因为什么时候你都没有办法预知

用户输入数据n值到底是多少 不能精确

你就没有办法给它分配恰当的内存

你说我尽可能得多

那没完没了一个事情

你不可能尽可能的

因为你不管给我多个数

我总能找到一个比它更大的数

所以说你必须能够恰当地分配

这才合适

不这么做 它就不恰当 那怎么办

你编译的时候程序是没有执行的

你压根不知道那个值

所以静态的不成 自动也不成

那我们怎么办 我们就要动态分配

只有动态分配才能够刚好地

给我们解决这个问题

你想分多大 我就给你多大

你要多少 给你多少

只要我们内存是足够的

那我就能够给你分

所有这些动态内存分配

都在一个专门的存储区

我们称它为堆的这样一个里面

它会涉及到两个主要的关键技术

一个我们会使用一个指针

来指向动态分配的内存区域

第二个就要使用引领操作符

来操纵目标数据对象

也就是说 没有指针

这个动态内存分配是做不到的

那我们在C里面

怎么对它进行动态内存分配呢

C里面提供这个函数malloc和free

就可以完成我们的动态内存分配

这两个函数原型定义在“cstdlib”和“cmalloc”

这两个头文件里边

这两个头文件里边你包含一个就够了

分配是malloc 释放是free

相应的分配函数不是malloc一个

是提供了好几个

我们总常用的是malloc

它会接受一个参数

就表示你分配多少个字节

size 你就传给它

你想分配5个字节

OK 你就传5个进去

你要分配4个字节

你就传4进去

你说我分配一个整数

整数多大呢 4个字节 也不一定

我们用的现在主流计算机

都是64位计算机 不是32位计算机

但是我们的主流编译器

还是32位编译器

所以分配一个整数就是4个字节

你也不能想当然总是4个字节

所以你要想确保能够正确的分配

sizeof(int)传进去 它是以字节为单位

你分配多少个字节 然后它就传回给你

它给你分配好的动态内存区域的基地址

free 就销毁它

你这么动态内存分配的一个区域

后来你又不要了

不要了 你就把那个基地址

通过memblock这个指针传进去

那个目标区域 就被你销毁掉了

你注意看 我们这里面

非常重要的一个指针类型 void *

这是一个非常非常非常重要的数据类型

我称它为哑型指针 它非常特殊

它表示我们的目标数据对象类型

是未知的 它是一个指针没错

指针的目标数据对象类型void也没错

不是说它的目标数据对象没有类型

目标数据对象一定是有类型的

但是它的类型是什么呢 我不知道

所以你绝不可以在这个指针上边

通过引领操作符

去访问它的目标数据对象

但是你可以把它转换成

指向其它类型的一个指针

转型完了以后

再去访问它的目标数据对象

这就可以了

但是转型操作是不是对的

那你程序员得保证它是对的

你如果保证不了 你这个程序就写错了

它的最主要的目的

就是作为一种通用的指针类型

首先 你可以用它来构造一个

指针数据对象和我们的目标数据对象的

一个一般性的关联

我就构造它 我用一个指针

我指向一个东西 那个东西类型是什么

我现在还不知道

OK我就用void *指针 然后指向它

后来我知道了它是什么类型

或者我需要详细地刻画

它到底是什么数据类型 比如是int *,是个char *

OK 我就把这个void *指针转换成int *

或者转换成char *

然后我就引领这个目标数据对象

这就是void *最重要的一个地方

所以在我们C语言里边

它是通用的类型

什么东西都可以表示

因为如果它不能够表示

它一定能够表示那个东西的地址

你比如讲那个结构体 void *不能表示

但是我能够表示指向结构体的指针

能够表示那个结构体的地址啊

int *它也能够表示

实际上 因为在32位编译器下边

它这个void *指针

尺寸是固定的 4个字节

如果int刚好也是四个字节

int就直接可以转换成void *

反正尺寸是够的 你直接传进去就行了

它是通用的一个型

malloc这个函数怎么用呢

用法有一个特别的规矩

你就按照这个规矩来

假设我要存储一个字符串

我就定义一个指针类型的量char * p

然后我调用malloc为它分配内存

比如说我想分配一个

能够保存10个字符的一个字符串

那么我就应该这么写:malloc(11)

因为我要分配11个字节才够用

后面还有一个‘\0’呢 你必须加上

它返回那个指针类型是void *

所以我们要把它转换成char *

才能赋值给p这个指针量

malloc这个参数里面

就是你要分配的存储空间的大小

以字节为单位 这样的话

它这个分配好的指针

p所指向的那片存储区里面

就能够保存10个字符的一个字符串

后面会封装一个‘\0’的

我们看这样一个例子

让同学们写一个函数

来完成字符串赋值的一个动作

DuplicateString我要复制它

传的是一个char * s进来的

然后我要复制出去

形成一个副本 我要拷贝

这个地方指针是不能够直接赋值的

如果你指针直接赋值

那实际上就让两个指针

都指向目标字符串了

你就没有做出这个副本来

像复印机 我要复印

复印出来那个结果要有一个原件

还有一个复件

我本来有一个原件

还有一个复印件出来了

所以这就叫做一个副本

你如果是直接两个指针赋值过去

s赋值给t 那不叫副本

你是让两个指针指向同一个东西

那个t就表示“参见什么什么东西”

我们这个复制 拿到原始的串

然后根据那个原始字符串的大小

为它分配恰当的存储空间

然后一个字节、一个字节给我拷贝过去

包括最后‘\0’ 我们也给它构造进去

然后返回构造出来的那个串才可以

而这个串的构造

我们一定要动态地构造

因为在调用这个函数之前

我压根就不知道s所指向的那个串

有多少个字符

所以你没有办法预先假定

你新的串的存储空间要多大合适

你只能按照这个方式来

给我多长我就分配多长

所以我们要strlen 调用这个函数

来获取这个字符串的串长

获取这个字符串的串长之后

把这个串长这个值给n 然后malloc

分配n+1个字符的存储空间

然后把它返回的void *哑型指针

转换成char * 赋值给t

然后我写一个for循环

从s中给它拷贝到t里面去

在t的尾部封装一个‘\0’

return指针t就可以了

这个函数的实现非常重要

它给我们提供了一个很特殊一个机制

你看到我们所有的字符串

都要按照这样一个机制来进行实现

你要分配一个特定的存储区域

来存储目标串

而这个目标串的区域的大小

实际上和我们原始串

这个信息是相关的

我们要先得到这个原始串的串长信息

然后动态分配这个区域空间

构造它的目标串

也就是说 如果你的原始串是10个字符

那我就构造11个字节的存储空间

能够刚好存那个10个字符加‘\0’

如果你的原始串是20个字符

那么我们就为你分配21个字节

刚好存20个字符加上一个‘\0’

就是一个字符不多 一个字符不少

你不按照这个方式来

你就没有办法保证这一点

动态内存分配就能够做到这一点

这是非常有益的一个特性

相对应地 就是free这个函数

你刚才分配内存

当你不想用了 你就得free 就得要销毁它

只有动态内存才需要销毁

因为这个内存你是malloc出来的

你才需要free

如果不是malloc出来的 是不需要free的

你看我刚才那个例子

如果你真分配了这段内存

p作为指向字符一个指针

然后你分配了11个字节给它

后来你不用了

那么你就想销毁这个字符串

你就free(p)就可以了

注意销毁的不是p所代表这个指针量

销毁的是这个p所指向的

那个目标字符串那个存储空间

而不是p这个指针量本身的存储空间

p本身不需要被销毁

它要么是局部量 要么是全局量

第二个例子

你要想分配10个整数存储空间

那你就malloc(10*sizeof(int))

分配40个字节 转换成int *

指向整数的指针 初始化给p

然后free(p) 你就会销毁这40个字节

注意 p虽然是指向这个数组的0号元的

但是因为我们通过这个指针

实际上操纵的是40个字节的存储空间

所以一销毁就销毁这数组整体

而不是只是销毁这个数组的0号元

编译器不会干这个事情

一销毁就销毁这个数组的整体

所以同学们在使用这个

动态内存分配的时候

一定要记得有了malloc你就要有free

就是说你有了分配你就应该有释放

也叫有借有还 再借不难

整个计算机内存就那么多

你malloc一部分内存 后来你不用了

其实你就应该把它free给操作系统

然后以后它可以再次再利用

如果你总是malloc 你从来不free

如果你程序运行时间很长

你内存空间很容易就不够了

程序就没办法运行下去了嘛

程序做得不好 那可能就会崩溃掉

还有一点需要特别注意的就是什么呢

你free(p)以后 这个p作为一个指针量

它其实仍然指向你那串被你分配出来的

后来又被你销毁的那片存储区

p的值是存在的

但是那段内存区域已经无效了

所以在这种时候同学们要记得

free(p)之后最好紧跟着

完成一次p=NULL的指令

这样的话你写出来的程序才是最恰当的

基于Linux的C++课程列表:

第一讲 C/C++基本语法元素

-1.1 提纲

--LinuxCPP0101

-1.2 程序设计的基本概念

--LinuxCPP0102

-1.3 简单C/C++程序介绍

--LinuxCPP0103

-1.4 程序设计的基本流程

--LinuxCPP0104

-1.5 基本语法元素

--LinuxCPP0105

-1.6 程序设计风格

--LinuxCPP0106

-1.7 编程实践

--LinuxCPP0107

-第一讲 C/C++基本语法元素--编程实践提交入口

第二讲 程序控制结构

-2.1 提纲

--LinuxCPP0201

-2.2 结构化程序设计基础

--LinuxCPP0202

-2.3 布尔数据

--LinuxCPP0203

-2.4 分支结构

--LinuxCPP0204

-2.5 break语句

--LinuxCPP0205

-2.6 循环结构

--LinuxCPP0206

-2.7 编程实践

--LinuxCPP0207

-第二讲 程序控制结构--编程实践提交入口

第三讲 函数

-3.1 提纲

--LinuxCPP0301

-3.2 函数声明、调用与定义

--LinuxCPP0302

-3.3 函数调用栈框架

--LinuxCPP0303

-3.4 编程实践

--LinuxCPP0304

-第三讲 函数--编程实践提交入口

第四讲 算法

-4.1 提纲

--LinuxCPP0401

-4.2 算法概念与特征

--LinuxCPP0402

-4.3 算法描述

--LinuxCPP0403

-4.4 算法设计与实现

--LinuxCPP0404

-4.5 递归算法(一)

--LinuxCPP0405

-4.6 递归算法(二)

--LinuxCPP0406

-4.7 容错与计算复杂度

--LinuxCPP0407

-4.8 编程实践

--LinuxCPP0408

-第四讲 算法--编程实践提交入口

第五讲 程序组织与开发方法

-5.1 提纲

--LinuxCPP0501

-5.2 库与接口

--LinuxCPP0502

-5.3 随机数库(一)

--LinuxCPP0503

-5.4 随机数库(二)

--LinuxCPP0504

-5.5 作用域与生存期

--LinuxCPP0505

-5.6 典型软件开发流程(一)

--LinuxCPP0506

-5.7 典型软件开发流程(二)

--LinuxCPP0507

-5.8 编程实践

--LinuxCPP0508

-第五讲 程序组织与开发方法--编程实践提交入口

第六讲 复合数据类型

-6.1 提纲

--LinuxCPP0601

-6.2 字符

--LinuxCPP0602

-6.3 数组(一)

--LinuxCPP0603

-6.4 数组(二)

--LinuxCPP0604

-6.5 结构体

--LinuxCPP0605

-6.6 编程实践

--LinuxCPP0606

-第六讲 复合数据类型--编程实践提交入口

第七讲 指针与引用

-7.1 提纲

--LinuxCPP0701

-7.2 指针基本概念

--LinuxCPP0702

-7.3 指针与函数

--LinuxCPP0703

-7.4 指针与复合数据类型(一)

--LinuxCPP0704

-7.5 指针与复合数据类型(二)

--LinuxCPP0705

-7.6 字符串

--LinuxCPP0706

-7.7 动态存储管理(一)

--LinuxCPP0707

-7.8 动态存储管理(二)

--LinuxCPP0708

-7.9 引用

--LinuxCPP0709

-7.10 编程实践

--LinuxCPP0710

-第七讲 指针与引用--编程实践提交入口

第八讲 链表与程序抽象

-8.1 提纲

--LinuxCPP0801

-8.2 数据抽象(一)

--LinuxCPP0802

-8.3 数据抽象(二)

--LinuxCPP0803

-8.4 链表(一)

--LinuxCPP0804

-8.5 链表(二)

--LinuxCPP0805

-8.6 链表(三)

--LinuxCPP0806

-8.7 链表(四)

--LinuxCPP0807

-8.8 函数指针(一)

--LinuxCPP0808

-8.9 函数指针(二)

--LinuxCPP0809

-8.10 抽象链表(一)

--LinuxCPP0810

-8.11 抽象链表(二)

--LinuxCPP0811

-8.12 编程实践

--LinuxCPP0812

-第八讲 链表与程序抽象--编程实践提交入口

第九讲 类与对象

-9.1 提纲

--LinuxCPP0901

-9.2 程序抽象与面向对象

--LinuxCPP0902

-9.3 类类型

--LinuxCPP0903

-9.4 对象(一)

--LinuxCPP0904

-9.5 对象(二)

--LinuxCPP0905

-9.6 类与对象的成员(一)

--LinuxCPP0906

-9.7 类与对象的成员(二)

--LinuxCPP0907

-9.8 类与对象的成员(三)

--LinuxCPP0908

-9.9 继承(一)

--LinuxCPP0909

-9.10 继承(二)

--LinuxCPP0910

-9.11 继承(三)

--LinuxCPP0911

-9.12 多态(一)

--LinuxCPP0912

-9.13 多态(二)

--LinuxCPP0913

-9.14 编程实践

--LinuxCPP0914

-第九讲 类与对象--编程实践提交入口

第十讲 操作符重载

-10.1 提纲

--LinuxCPP1001

-10.2 四则运算符重载(一)

--LinuxCPP1002

-10.3 四则运算符重载(二)

--LinuxCPP1003

-10.4 关系与下标操作符重载

--LinuxCPP1004

-10.5 赋值操作符重载(一)

--LinuxCPP1005

-10.6 赋值操作符重载(二)

--LinuxCPP1006

-10.7 赋值操作符重载(三)

--LinuxCPP1007

-10.8 赋值操作符重载(四)

--LinuxCPP1008

-10.9 赋值操作符重载(五)

--LinuxCPP1009

-10.10 流操作符重载(一)

--LinuxCPP1010

-10.11 流操作符重载(二)

--LinuxCPP1011

-10.12 流操作符重载(三)

--LinuxCPP1012

-10.13 操作符重载总结

--LinuxCPP1013

-10.14 编程实践

--LinuxCPP1014

-第十讲 操作符重载--编程实践提交入口

第十一讲 泛型编程

-11.1 提纲

--LinuxCPP1101

-11.2 泛型编程概览

--LinuxCPP1102

-11.3 异常处理机制(一)

--LinuxCPP1103

-11.4 异常处理机制(二)

--LinuxCPP1104

-11.5 运行期型式信息(一)

--LinuxCPP1105

-11.6 运行期型式信息(二)

--LinuxCPP1106

-11.7 模板与型式参数化

--LinuxCPP1107

-11.8 题外话:术语翻译

--LinuxCPP1108

-11.9 泛型编程实践(一)

--LinuxCPP1109

-11.10 泛型编程实践(二)

--LinuxCPP1110

-11.11 泛型编程实践(三)

--LinuxCPP1111

-11.12 泛型编程实践(四)

--LinuxCPP1112

-11.13 泛型编程实践(五)

--LinuxCPP1113

-11.14 泛型编程实践(六)

--LinuxCPP1114

-11.15 泛型编程实践(七)

--LinuxCPP1115

-11.16 泛型编程实践(八)

--LinuxCPP1116

-11.17 泛型编程实践(九)

--LinuxCPP1117

-11.18 泛型编程实践(十)

--LinuxCPP1118

-11.19 编程实践

--LinuxCPP1119

-第十一讲 泛型编程--编程实践提交入口

第十二讲 Linux系统编程基础

-12.1 提纲

--LinuxCPP1201

-12.2 程序执行环境(一)

--LinuxCPP1202

-12.3 程序执行环境(二)

--LinuxCPP1203

-12.4 程序执行环境(三)

--LinuxCPP1204

-12.5 程序执行环境(四)

--LinuxCPP1205

-12.6 输入输出(一)

--LinuxCPP1206

-12.7 输入输出(二)

--LinuxCPP1207

-12.8 文件系统

--LinuxCPP1208

-12.9 设备

--LinuxCPP1209

-12.10 库(一)

--LinuxCPP1210

-12.11 库(二)

--LinuxCPP1211

-12.12 makefile文件(一)

--LinuxCPP1212

-12.13 makefile文件(二)

--LinuxCPP1213

-12.14 makefile文件(三)

--LinuxCPP1214

-12.15 编程实践

--LinuxCPP1215

-第十二讲 Linux系统编程基础--编程实践提交入口

第十三讲 进程编程

-13.01 提纲

--LinuxCPP1301

-13.02 进程基本概念

--LinuxCPP1302

-13.03 信号

--LinuxCPP1303

-13.04 进程管理(一)

--LinuxCPP1304

-13.05 进程管理(二)

--LinuxCPP1305

-13.06 进程管理(三)

--LinuxCPP1306

-13.07 进程间通信(一)

--LinuxCPP1307

-13.08 进程间通信(二)

--LinuxCPP1308

-13.09 进程间通信(三)

--LinuxCPP1309

-13.10 进程间通信(四)

--LinuxCPP1310

-13.11 进程池

--LinuxCPP1311

-13.12 编程实践

--LinuxCPP1312

-第十三讲 进程编程--编程实践提交入口

第十四讲 线程编程

-14.1 提纲

--LinuxCPP1401

-14.2 线程基本概念

--LinuxCPP1402

-14.3 线程管理(一)

--LinuxCPP1403

-14.4 线程管理(二)

--LinuxCPP1404

-14.5 线程管理(三)

--LinuxCPP1405

-14.6 线程管理(四)

--LinuxCPP1406

-14.7 线程同步机制(一)

--LinuxCPP1407

-14.8 线程同步机制(二)

--LinuxCPP1408

-14.9 C++11线程库(一)

--LinuxCPP1409

-14.10 C++11线程库(二)

--LinuxCPP1410

-14.11 C++11线程库(三)

--LinuxCPP1411

-14.12 C++11线程库(四)

--LinuxCPP1412

-14.13 C++11线程库(五)

--LinuxCPP1413

-14.14 编程实践

--LinuxCPP1414

-第十四讲 线程编程--编程实践提交入口

第十五讲 网络编程

-15.1 提纲

--LinuxCPP1501

-15.2 Internet网络协议

--LinuxCPP1502

-15.3 套接字(一)

--LinuxCPP1503

-15.4 套接字(二)

--LinuxCPP1504

-15.5 编程实践

--LinuxCPP1505

-第十五讲 网络编程--编程实践提交入口

课程文档

-课程PDF文件

LinuxCPP0707笔记与讨论

也许你还感兴趣的课程:

© 柠檬大学-慕课导航 课程版权归原始院校所有,
本网站仅通过互联网进行慕课课程索引,不提供在线课程学习和视频,请同学们点击报名到课程提供网站进行学习。