当前课程知识点:基于Linux的C++ >  第七讲 指针与引用 >  7.2 指针基本概念 >  LinuxCPP0702

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

LinuxCPP0702在线视频

LinuxCPP0702

下一节:LinuxCPP0703

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

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

我们首先来看指针

这里面涉及到一个重要的地方

我们前面已经谈到过

每一个变量 也就是说每个数据对象

它有四个基本特征:VANT

一旦你的程序编译完了以后

N和T 也就是它的名称和类型就没了

正常情况下 信息是缺失的

名称会缺失 类型会缺失

在我们的程序代码里边

实际上主要就那么两个东西:

一个是地址 一个是值

地址就是数据对象放在什么位置

值就是在内存区那个位置里

放了什么东西

这就是两个东西 地址和值

把地址和值完全孤立去看待它们

这是不可以的

地址就是地址 值就是值

两者之间完全没有什么关联

你如果按照一种孤立的、僵化的、

机械的观点来看待

这一定是不对的

它不符合我们的马克思主义哲学原理

地址和值是辩证统一的关系

同学们一定要记住这句话

通过一种恰当的机制

我们可以让一个量的地址就是值

可以让一个值就是一个地址

这个机制叫什么呢

同学们现在已经知道了 它叫指针

为什么要讲指针

就讲这个东西的——它就是构造地址和值

辩证统一的最重要的一个桥梁

有了这个概念之后

我们就可以来定义指针类型的变量

使用指针类型的变量

然后我们就会讨论指针到底和函数、

复合数据类型、数组、结构体

具有什么样的关系

在此过程中 我们还需要了解

我们怎么进行简单的指针运算

这个就是我们涉及到的

最基本的指针的内容

后面实际上我们讲字符串

讲动态存储管理

其实仍然还会涉及到指针

包括我们下一讲程序抽象

其实也仍然要涉及到指针

我们首先来看指针的定义或使用

你真定义一个指针变量的话

它的方法其实是非常简单的

就是一个目标数据对象类型、“*”、

指针变量的名字

你比如讲第一个例子 int * p

我就定义了一个指针类型的变量

名字叫p 它是一个指针 指向一个整数

把这个东西倒过来看

定义一个变量 名字叫p

它是一个指针 指向一个整数

你如果有一个struct POINT结构体POINT *p

那么你就定义一个指针变量p

它是一个指针

指向一个结构体POINT 很清楚吧

如果你想定义多个指针变量

那么你必须按照这个方式写:int *p, *q

这样的话p是一个指针 指向一个整数

q是一个指针 指向一个整数

这是非常明确的定义

第二个“*”不能少了

如果第二个“*”没了 写成int * p, q

那么这就意味着

p将是一个指针指向一个整数

而q就是一个整数 它不是一个指针

注意这个概念

初学者非常容易在这个地方犯错误

会忘了写第二个“*”

怎么解决这个问题呢

怎么确保自己不忘呢

第一个 编程你要仔细

除此之外就是用第二种方案——用typedef

把int*给重新定义成PINT

创建一个新的类型PINT

让它等价于int *

我们前面讲typedef这个关键字的时候

特别谈到过

typedef将为我们创建一个新的类型

新的类型将和原始的类型一模一样

也就是说 在我们程序代码里边

凡是能够出现PINT地方

都可以用int *来代替

凡是能够出现int *的地方

都可以用PINT来代替

两者是完全等价的

我们讲typedef的时候就这么谈到过

但是你要记住

这个等价并不是文本上的等价

也就是说你不能把这个例三

这个地方的int *替换成PINT去编译

有了PINT这个类型的定义

那我就写PINT p, q

这样的东西 就会为我们定义两个

指向整数的指针变量

p是个指针 指向一个整数

q还是一个指针 指向一个整数

这一点和前面这个定义是不一样的

你说如果我简单文本替换

我把int *替换成PINT

第三个例子 那就是PINT p, *q

q就是一个指针

它指向什么呢 它可不指向整数

同学们记住了

q那个时候将会指向PINT

也就是 q将是一个指针

它将指向一个指针

而那个指针才指向一个整数 它不是p

p它是一个指针指向一个整数

注意这个概念

所以这个时候如果你写PINT p, *q

那么q将是一个指向指针的指针

绝不是指向整数的指针

注意这个概念

所以一旦有了typedef PINT出来

那么p、q这么定义就是两个指针了

如果你没有typedef这个定义

那么你就必须写int * p, * q

在使用指针的时候 同学们一定要记住

指针数据对象事实上涉及到了

两个数据对象 并不是只有一个

一个就是指针数据对象本身

第二个就是指针所指向的这个

目标数据对象 你比如说讲

我用int * p定义一个指针变量p

它将指向一个整数

你如果按照这样方式来定义它

你也没有对它进行初始化

也没有对它进行赋值的动作

那么在你的内存里边

就只会分配一个指针变量的地址空间

假设我们把它随便放在内存某个地方

然后旁边写个p

OK 这是一个指针变量存储布局

它里面什么东西都没有

因为我们没有初始化

也没有对它进行赋值

所以什么也没有

如果你在定义这个指针变量过程中

同时对它进行初始化

那么这个时候就会涉及到两个数据对象

假设我有一个整型量n被初始化成了10

然后有一个指针p指向这个整数

并且把这个指针p初始化为n这个

整数的基地址 就这么写 int * p = &n

把n的地址传给p

我们初始化的是这个指针变量p

n会放在内存中的某个位置

假设这个地址的编号是0x00130000

里边的内容是10

p被初始化的时候 将会保存n的基地址

n的基地址是0x00130000

所以p里边将会保存0x00130000

计算机硬件架构有一个特殊的功能

有一个间接访问机制

我们可以使用间接访问机制通过p

来访问p所指向这个目标数据对象n

为什么说是指向呢

你看这个图 n放在内存某个地方

n的内容里面是有的 地址空间是有的

而p呢 它里面存的就是n的地址

所以在看上去构造了一个p和变量n

两者之间的一个指向关系吗

我们就通过计算机间接访问机制

通过p来访问n的

正是从这个角度上来讲

p和n通过初始化的动作

构造了一个指向的关联

而这个就是我们这个东西

为什么叫指针的关键的地方

就是因为这个指向的关联

这个是和计算机的间接寻址的机制

完全地吻合在一块的

如果我们定义一个指针变量

让它指向一个数组中的一个元素

和我们指向一个整数非常得相似

有一个整数数组a

它里面包括8个整数 要对它进行初始化

指向整数的指针p

我要把指针初始化为这个数组a的基地址

因为这是个数组的名字

所以前面取它的基地址的时候

前面要不要加“&”都一样

假设我这个数组的起始位置是0x00130000

也就是说它的0号元就放在这个位置

让p这个指针指向这个数组的基地址

指向这个数组的0号元

p的内容就会写入0号元的基地址

0x00130000

就构造了这个指针p和数组0号元的

一个指向关系

p作为一个指针

将指向数组的a的0号元

也就是说p作为一个指针

并不指向这个数组

按照这样方式定义的指针p

并不指向这个数组

仅仅指向那个数组的0号元

因为p作为一个指针

它只能指向一个整数

它并不能够指向8个整数

所以我们用数组的基地址赋值给它

初始化给p

那么它将指向这个数组的0号元

这就是指针变量的存储布局

接下来 如果你有两个指针量

有一个整数量n 两个指针量p、q

p这个指针量被你初始化成n的基地址

然后你做了一个赋值的动作

把p赋值给q 这个赋值是允许的

因为指针变量可以像普通的变量一样赋值

这样的话两个指针将会指向

同一个数据对象n

p一开始是指向n的

假设这个变量n的存储位置是0x00130000

p的内容将会被初始化为n的基地址

把p的内容赋值给q

那q里面当然也是0x00130000

q和p值是一样的

这个时候就意味着

两个指针指向同一个目标数据对象

接下来我们就来讨论

刚才用到的取址操作符“&”

“&”这个操作符

它的目的是获取这个数据对象的地址

得到这个地址

像一个普通的值一样赋值给指针变量

就我前面讲的地址和值辩证统一地来看

就是把这个地址作为值

赋值给另外一个指针变量

把int n初始化成10 我定义指针变量p

int * p 然后把p赋值为&n

把n的地址赋值给p

这就是一个非常典型的动作

如果还有一个指针变量q

也是一个int *

然后你把p再赋值给q

或者把&n赋值给q

那么得到的结果也一样

你就会让这两个指针

指向同一个数据对象

接下来 最重要的一个操作符——引领操作符

我们想用指针

来访问指向的目标数据对象的话

就必须使用引领操作符

定义两个整数m和n

n的初始化成了10

然后我定义指向整数的一个指针p

然后把它初始化成n的基地址

用*p去引领p所指向的目标数据对象

p指向n 那*p就代表着这个指针p

所指向的目标数据对象n

你想取*p的内容 取的就是10

你就可以把n的内容写到m里

我们来看这个整数互换的例子

我们编写一个程序

使用指针来互换两个整数的值

这个程序的代码很简单

我们的main函数里面就这么几条语句

我定义了三个量m、n、t

然后定义了两个指针量p、q

三个整数量m、n、t

两个指针量p、q

我把p初始化为m的基地址

q初始化为n的基地址

m被我设成了10

n被我设成了20

这就是我们的五个基本量

然后我输出m、n的数据

接下来三步标准互换

注意我是通过引领操作符

来操纵指针所指向的目标数据对象

完成这个标准的三步互换的

然后我再输出m和n的值

看看我们互换后的结果是什么样子

程序实际运行的过程中

我们可以划出这样一个框图来

我这里面五个数据对象m、n、t、p、q

m是10,n是20

然后p指向的是m

q将会指向n

你注意看指针指向关系

当我完成这样的一个指针布局之后

调用我们的三步互换操作

t = *p 一旦把*p赋值给t

就意味着事实上是把m的内容传给t

t就会变成10

接下来第二步我把*q赋值给*p

*q是什么呢 是n

*p是什么呢 *p是m啊

*q就是n啊

所以我把*q赋值给*p

就意味着是把n的值赋值给m

m就会变成20

第三步 把t赋值给*q

那么*q的内容就会变成10

这就是完整的三步互换

你看到程序运行的时候

我们存储空间的这个栈框架变化了吧

p和q维持着指向m和n的

指针指向关系是不变的

在整个程序的执行过程中

p、q这两个指针

它们的指向关系都是不变的

p一直指向m,q一直指向n

但是因为我们使用了引领操作符

结果导致m和n的值发生了互换

m变成了20,n变成了10

m和n的值互换了

p、q它们的指向关系没有发生变化

这个就是通过引领操作符

进行标准的三步互换的时候

所产生的结果

我们刚才那个整数互换的例子

是使用引领操作符

互换了目标数据对象的值

然后我们来看同样一个程序

如果我直接互换的是

两个指针数据对象的值

会发生什么事情

现在这个程序代码

需要定义两个整型变量

和三个指针变量p、q、t

因为我要互换的是指针对象的值

所以三步互换t值就不能是整型量了

必须是一个指针量

p仍然指向m,q仍然是指向n

t的指针量会赋值为p

q给p,t给q

我们来看它会发生什么事

一开始指针的存储布局

p指向m,q指向n

t值开始是没有的

当你把p赋值给t的时候

t值的内容就会变成&m

当我做第二步 q赋值给p的时候

p内容就会变成q的内容

p就从&m变成&n

指针的指向关系就会发生变化

p值将不再指向m 它将指向n

第三步 当我把t赋值给q的时候

q将会指向m

三步互换做完 我互换的是p、q的值

也就是互换p、q的指向关系

但是我没有互换m和n的值

当你想真正互换m和n值的时候

一定要使用引领操作符

指针的使用场合就四个

一 指针作为函数通讯的一种手段

可以使用指针作为我的函数参数

不仅可以提高参数的传递效率

还可以让参数作为函数的输出集的一员

把结果带回来

和外界进行通讯的时候

函数可以有几个返回值呢

一个或者没有 它只能带回来一个结果

如果一个函数

想带回来两个以上的结果怎么办呢

把它两个结果合成一个结构体

用这个结构体作为函数的返回值

它就可以把这个同时都带回来

还有一种方案 指针作为函数参数

它就可以把这个结果带回来

第二个指针的使用场合

作为构造复杂数据结构的手段

使用指针构造数据对象之间的关联

可以形成一个极其复杂的数据结构

第三个作为动态的

内存分配和管理的手段

可以在程序的执行期间

动态构造数据对象之间的关联

第四个是作为执行特定的程序代码手段

我可以使用一个指针指向一段程序代码

来执行未来才能够实现的函数

这四个使用场合同学们一定要会用

基于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文件

LinuxCPP0702笔记与讨论

也许你还感兴趣的课程:

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