当前课程知识点:基于Linux的C++ >  第七讲 指针与引用 >  7.3 指针与函数 >  LinuxCPP0703

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

LinuxCPP0703在线视频

LinuxCPP0703

下一节:LinuxCPP0704

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

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

我们现在就来看指针和函数的关系

一个是数据交换函数的例子

第二个就是常量指针和指针常量的

第三个如果一个函数

它要返回一个指针 我怎么办

我们先来看数据互换函数

编程互换两个整型数据对象的值

现在我来使用函数来实现它

Swap这个函数使用两个指针形式的

形式参数 int *x、int *y

一个函数调用Swap( &m, &n )

因为我要传的是两个整数的地址

所以我调用Swap这个函数的时候

应该传&m、&n

我这个程序呢

前面打了一个cout 后面也打一个cout

是要把这个m和n的值给打印出来

然后来看Swap这个函数的调用的前后

m和n值有没有发生变化

这里面使用了个很特殊的技巧

#ifndef NDEBUG、#endif

把cout语句给封装起来了

#ifndef NDEBUG、#endif

这个东西叫什么呢

它是一个条件编译指令

它作为一个宏测试 #ifndef

意思就是如果没有定义这个宏NDEBUG

那么它就会编译这段代码

程序运行的时候就会执行这段代码

#endif就结束这个条件判断

所以如果你要想编译这段代码

就把段代码写在这里

真实编程的时候

如果后来你又不想要cout代码了

不用删除

只需要在整个程序代码的前边

写上一句#define NDEBUG

定义一下这个宏

如果没有定义 它就会测试失败

一旦测试失败 这个分支就不做了

它有什么好处

好处就体现在:当这个程序代码中

插入了很多条类似的cout语句

目的就是为了测试

显示一下中间输出结果对不对

就给你观察用的 调试用的

这个程序正式发布了 没问题了

这些中间结果你都不应该输出

不应该输出 你不需要删除

一个一个去找 哪个地方该输出

哪个地方不该输出

如果代码量很大 很难找的

有的时候没准就搞错的

所以很简单

如果你在写程序的时候一开始就决定

这些代码就临时输出看一看

调试的时候看一看对不对

一旦调试完毕都不应该出现

那么就应该把这样的代码

封装在#ifndef NDEBUG、#endif指令中就行了

然后这些指令都不想要了

你就前面直接写上#define NDEBUG

它就自动地不编译了

这个为我们省很多事

我们来看这段代码

Swap这个函数带了int *x,int *y

这样的两个形式参数

我们会使用带引领操作符的

三步互换操作

完成x、y这两个指针

所指向目标数据对象值的互换

在这个三步互换前后

我依然用cout输出它的x、y所指向的

目标数据对象的值

也依然把这两条cout语句

封装在#ifndef NDEBUG、#endif之间

这个就是标准的Swap函数

在这里要保证x和y这两个指针

是合法有效的

所以前面我要有一个if测试

和我们早期讲函数的时候

整数互换那个例子不一样

看这个函数调用的栈框架

在main函数里边有两个量m和n

m是10,n是20

我们假设放在内存条的某个地方——

底下这一半是内存条

m是放在一个地方 n是放在一个地方

内存条里m和n是存在的

它就有一个数据值

上面是栈框架 它表示m、n是属于main

接下来我调用Swap这个函数

它里边其实就俩参数

x和y,x是&m,y是&n

当这个Swap函数在运行的过程中

x和y也依然会放在内存条的某个地方

假设是在这个位置

Swap这个函数

实际上是覆盖了这个main函数的栈框架了

所以在Swap函数执行的过程当中

你是看不见main函数的数据的

实际上是不能访问m和n的值的

也就是说 你不能在Swap函数里边

使用main函数的m 也不能使用main函数的n

这两个量你是用不了的

并不意味着它真地不存在

只是我们没有办法

通过名字m和n来访问它们

现在我们有指针

我用*x、*y就可以访问

*x就是m,*y就是n

所以在Swap函数里边

凡是对*x、*y的操作

都会自动地反映到m和n里面去

Swap函数中的x和y这两个参数

能够把结果带出去

一旦你完成这个三步互换

m和n的值就会互换

你真互换m和n的值吗 没有

从整个程序设计的角度上看——

你从源代码上看

它实际上互换*x和*y的值

我们没有直接操作m和n

是用x和y间接操作的m和n

但是因为*x就代表m,*y就代表n

所以我们在Swap函数里边

对*x、*y所做的任何改变

都能够立即地反映到m和n里边去

而m和n从属于main函数

这就意味着Swap这个函数

通过使用两个指针作为函数的形式参数

能够将结果带出去

这就是它(指针作为函数参数)的第一个原因

不仅如此 参数传递效率还会高一些

对于整数来讲 当然不会高

但是有一些特定的量

它的参数传递效率就会高了

一个结构体的尺寸往往会很大

你要传结构体 那么结构体得整体赋值

你如果传指向结构体的指针

那么它只需要赋结构体的地址就行了

结构体的地址有多大呢

我们32位的计算机、32位编译器

它的尺寸是固定的 4个字节

所以当这个数据结构很大的时候

我们传递它的地址 而不是传递它的值

会让它的参数传递效率提高很多

接下来这个技术细节

就是常量指针或指针常量

如果有这样一种情况

指针指向一个常量 会发生什么事情

这样一个指针指向的是一个固定的常量

它的值是不可以发生改变的

你不可以通过引领操作符

来修改目标数据对象的值

int n初始化为10

cons tint *p初始化为&n

看这个示例一

你按照这样一个方式来定义指针变量p

指针将指向const int

不能通过*p去修改它的内容

想通过*p赋值为20这种方式

想把n的值改成20

这个方式是做不到的

因为它指向一个常量

所以它不可以被赋值

哪怕n值本身是一个整型量

而不是一个常量

你也不能通过*p修改n的值

这就是常量指针告诉我们的事情

它最典型的使用场合

作为函数的参数

来表示函数内部不会通过引领操作符

修改目标数据对象的值

有一个函数 名字叫PrintObject

完成特定对象的打印

输出目标数据对象的值和它的内容

不会改变它的

这个时候形式参数写const int *p

不应该写int *p

只写int *p就可能会以为函数

在内部通过*p修改目标数据对象的值

和我们函数本身的意义是不符的

PrintObject打印就行了 不会修改的

你就应该在写函数原型的时候

把不通过引领操作符

修改目标数据对象值的限定

明确地表示出来

这就是指向常量指针最关键的用法

限定了指针p

只能作为函数输入集的一部分

不能作为函数输出集的一个部分

这是第一种情况 常量指针

第二种情况 指针常量

指针指向的位置不可变化

指针只能指向固定一个地方

你必须按照这个方向来定义

int * const p要把const写在p的前面

“*”的后面

这个时候必须把它初始化成&n

n的地址传给它

因为p是一个常量 不可以被赋值

所以你定义它的瞬间必须初始化

刚才那个地方 你不初始化是可以的

然后再赋值

把p赋值为&n是可以的

这里因为p是一个常量

是不可以被赋值的

所以必须被初始化

它初始化成一个整数的地址

也就是说 p只能指向n

绝不可以指向其他的量

但是可以通过*p去修改n的值

*p本身是int

刚才*p是什么 const int

哪怕它指向的那个量本身是int

你也不能改 因为*p是const int

而这个*p是int 你是可以改的

第三种情况

如果指针只能指向一个固定的地方

同时那个地方里面的内容必须是固定的

这就叫常量指针常量

精确地说法就是指向常量的指针常量

这就意味着不仅指针量本身 还有目标量

两者都是双重的只读属性

有一个const int n初始化成10

此后定义const int * const p

初始化成n的基地址

就按照这种方式来定义

这就意味着 p不可以被赋值

*p也不可以被赋值

int和“*”这两个标记

前后 中间正好有三个空位

这里边有俩const

你写const int * const p是对的

这就是常量指针常量

你写int const * const也是对的

你就是不能写const int const * p

那是不对的

为啥 因为const这个关键字

实际上是左结合的

它作用在它的左边的标记的上面

你这么写 这个const作用在这个“*”上

你这么写 这个const作用在哪呢

作用在右边int上

你刚才不是说了 这不是左结合的吗

左边有啥 左边什么都没有的

所以它就只能作用它右边的int上面

它不作用在那个“*”上

它也不是作用在int *上

它只作用在那个int上面

表示那个整数常量

实际上 如果你真正地严格按照

const左结合的定义

const int应该写int const

按照这个写法才是对的

所以第一个const

写在int前和int后都是对的

第二个const一定要写在“*”的后边

指针量的名称的前边

最典型的使用场合作为函数的参数

虽然在C和C++编程里边

大部分情况下边

我们不需要写得这么复杂

只需要写一个const int *p就可以了

就表示不能通过*p

去操纵目标数据对象的值

第三个 指针与函数的返回值

指针是可以作为函数返回值的

它会带回来一个目标数据对象的地址

但是指针作为函数返回值的时候

它不能返回函数内部定义的

局部变量的地址

只能返回某个全局量的地址

或者作为函数的参数传给函数的指针

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

LinuxCPP0703笔记与讨论

也许你还感兴趣的课程:

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