当前课程知识点:基于Linux的C++ >  第八讲 链表与程序抽象 >  8.9 函数指针(二) >  LinuxCPP0809

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

LinuxCPP0809在线视频

LinuxCPP0809

下一节:LinuxCPP0810

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

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

我们来看这样一个例子

我让同学们设计一个程序

来随机生成八个10到99之间的整数

然后调用stdlib库里面的qsort

来对这8个整数进行排序

在C的标准库里面

qsort这个函数它的原型是这个样子

第一个是void* base

第二个是number_of_elements

第三是size_of_elements

第四个是compare 这四个参数

第一个参数表示

我们需要排序的数组的基地址

第二个参数表示

这个数组中所包含的元素的个数

第三个参数表示这个数组的每一个元素

所占的存储空间的大小 以字节为单位

第四个参数就是一个函数指针

它用于比较两个数据对象的大小关系

所以你看 这个qsort函数原型

它实际上是非常复杂的

我三行才把它写完嘛

因为我们这里面使用了一个函数指针变量

来作为函数的形式参数

这个是非常非常常见的一个编程设计技巧

同学们一定要会用

这个函数指针变量的描述是这样

int (*compare)(const void * e1, const void * e2)

e1和e2你可以省略 什么意思

我们将比较两个数据对象的大小关系

而这两个数据对象

我们将作为函数的参数传给compare

也就说compare在比较

两个数据对象大小关系的时候

这两个数据对象本身对它而言是已知的

我们实际上并没有传递那两个数据对象

而是传递指向那两个数据对象的指针

因为我们不允许你在compare这个函数里面

通过指针修改目标数据对象的值

所以这两个指针所指向的目标数据对象

都定义成了const void而不是void

这个就是我们的比较函数

当你想使用这个qsort函数的时候

你必须定义一个自己的比较函数

那个比较函数的格式

还必须按照这样的格式来

返回值是个int 函数名字随你

两个参数 一个叫const void * e1

一个叫做const void * e2

你必须按照这样一个方式

来定义这个比较函数

同时在实现这个比较函数的时候

你要记得

比较函数返回值应该是-1、1和0

如果x是大于y你返回1

那么就表示这个的函数

是从小到大排序

你必须按照像这样的一个格式

来定义compare所指向的

那个目标比较函数

那你看qsort这个函数原型四个参数

是不是很奇怪

为什么要这么去实现呢

它难道不能够在这个里面

实现一个比较函数吗

是的 它实现不了

qsort什么时候实现的

已经很有一些年头了

在C标准库出来的时候它就有了

那个时候实现这个qsort的那个程序员

他知道你用它来对

什么类型的数组进行排序吗 他不知道

也许是个整数数组

也许是个浮点数数组

也许是个结构体数组

如果是结构体数组的话

那显然是你定义的结构体

这个结构体是什么样子

他几十年怎么能够可能知道呢

他是不可能知道的 对吧

他不可能知道你未来才会实现的结构体

那么他哪里能够进行大小的排序呢

当他实现这个qsort函数的时候

当他需要对未来才会实现的

一个数组进行排序的时候

他显然没有办法决定

这两个元素之间的大小关系

在他不能够决定这两个元素的

大小关系情况下面

他又想实现qsort函数

那么他只好把这部分悬而未决的东西

留待实现数据结构的程序员来去做

因为这个函数没实现

你就不能够直接调用它

那么就只能通过函数指针来调用它

这个就是函数指针最重要的应用场合

就是作为另外一个函数的形式参数

我们来看程序代码

我们当然需要包含 “arrmanip”

这个“.h”头文件

我们要操纵数组

生成一些整数 然后要排序

所以需要这个头文件

定义了8个元素

然后我们要实现一个比较函数

int DoCompareObject(const void * e1, const void * e2)

这个函数原型

是我们的比较对象的函数原型

必须按照这个格式来

然后我们的主程序 定义这样一个数组

int a[NUMBER_OF_ELEMENTS]

然后GenerateIntegers

我们生成8个整数

然后打印一个提示信息

然后PrintIntegers

把这些整数数组给打印出来

然后我们调用qsort

然后再打印一遍

看看它的排序是不是正确

你看qsort怎么使用的

qsort这个函数调用

后面要带四个实际参数

第一个参数传待排序数组的基地址

所以我们传数组的名字

第二个参数传元素的个数

NUMBER_OF_ELEMENTS

第三个参数传元素的尺寸大小

所以我们传sizeof(int)

第四个参数传这个数组中的

两个元素的比较关系那个比较函数

要传我们这个数组里面的

两个元素的比较函数DoCompareObject

仍然是单独出现这个函数的名字

就表示取这个函数的入口地址

然后传给那个函数指针变量

这个函数跟在我们的main后面

就是这么写的

把e1和e2都转换成const int *

然后调用CompareInteger这个函数

来比较这两个整数的大小关系

注意我们必须把e1和e2

转换成const int *之后 才能去引领

你不能先引领后转换

你先引领的话*e1

它实际上表示什么呢 表示const void

然后你想把它转换成 const int

对不起 这事不能做

因为void类型和const void类型的量

它不能参与实际的操作 对吧

你想访问它 这是非法的 类型未知

所以必须首先把e1和e2转换成const int *

然后去引领才可以

只能按照这个方式来

同学们一定要记住

接下来 看看我们的“arrmanip”这个头

三个函数:GenerateIntegers、CompareInteger、

PrintIntegers

上下两个函数我们前面都见过

就剩下CompareInteger

虽说这个函数也超简单

“arrmanip.cpp”这个数组操纵的库

定义了两个静态的常量

lower_bound、upper_bound

用来表示10和99这个数组

元素生成的上界和下界

然后我们会生成它

这个函数原来实现过 我们就不解释了

然后是PrintIntegers

我们也实现过 不解释了

然后是CompareInteger

从小到大排序

if(x>y) return 1; else if(x==y) return 0;

else return -1;

我们就按照这个模式

完成CompareInteger函数的实现

接下来就是函数指针的最基本的使用

你可以想象

如果我们都是按照qsort那样一个格式

来书写我们的函数原型

一旦它里面使用到了函数指针类型的参数

那么这个函数原型将是极度复杂的

这个写法实际上是不太好的

可以把一个函数的入口地址

赋值给函数指针类型的变量

这是毫无疑问的 是可以的

就像我们前面看的as_string

把DoTransformObjectIntoString

这个函数的入口地址赋值给它 对吧

这是很正常的一件事情

那么你可能就会说

如果我有同一个函数指针类型的

两个函数指针变量

那两个函数指针变量之间

互相是不是可以赋值啊

那当然肯定是可以的 这是一点

第二点 一个特定的函数指针变量

它可以指向什么呢 它指向一个函数

那个函数是什么样子呢

它带有一个特定的顺序和特定数目、

特定类型的函数形式参数列表

返回值是一个特定类型

OK 这个我们就定义了一个特定的函数簇

这个函数簇里面所能包含的函数个数

它一定是很多很多的

这些函数中任意一个函数

其实都可以赋值给我们的函数值变量

所以 如果我们想表达这个函数簇的概念

那么我们就应该明确这个类型的函数

和另外一个类型的函数两者之间的差别

我们需要对它进行分类

分类就是类型化

类型化就是我们需要定义一个新的型出来

这个型就叫函数指针类型

就用来区分不同类型的函数指针

定义它的格式特简单

就是我们函数指针变量的定义格式

前面加上typedef 就OK了

前面有typedef

那么我们第一个小括号里面的那个名字

COMPARE_OBJECT就表示函数指针类型

没有typedef

那么COMPARE_OBJECT就是函数指针变量

就这点差别

当它表示类型的时候 我们把它全大写

变量的时候我使用全小写

我就区分它到底是类型还是变量

现在我就有了一个新的类型

这个类型的名字叫COMPARE_OBJECT

有了新的类型以后

你就可以把它像一个int

像一个double

像一个struct COMPLEX

这种类型一样 定义变量

有了变量就可以赋值

很明确 有了变量还可以初始化

一切都像普通变量一样操纵

你看COMPARE_OBJECT这个类型

后面定义一个变量compare

初始化成DoCompareObject这个函数

这是合法的

可以按照这样一个方式进行工作

有了这个函数指针类型的定义

qsort这个函数

那么我们就可以把它写得简单一点

void qsort( void * base,

unsigned int number_of_elements,

unsigned int size_of_elements,

COMPARE_OBJECT compare )

第四个参数

写法就和前三个参数一模一样

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

LinuxCPP0809笔记与讨论

也许你还感兴趣的课程:

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