当前课程知识点:数据结构 > 第0章 预备知识 > 0.4 数组、指向函数的指针 > 0.4 数组、指向函数的指针
同学们,大家好
我是云南大学信息学院的教师孔兵
这节我们讨论一下指针和数组
在c语言中因为数组实现的方式
数组和指针有很强的关联
数组名是数组的起始地址
这是一个常数,作为地址
我们可以把它看作是一个指针
示例中首先声明了一个包含5个整型数的数组a
a是数组名,是分配给数组空间的起始地址
声明了一个指向整型的指针p
下面看一下对数组的扫描操作
第1个循环当中,用的是大家比较熟悉的方式
也就是通过数组下标来扫描整个数组
并把数组的内容显示出来
第2个循环中
是通过指针来扫描数组的
首先,p被赋值a
也就是说让p指向数组的起始地址
当p小于a+5的时候进行循环
p是一个整型指针
*p就确定了从起始地址开始的4字节的空间
也就是数组中0号下标单元的这个整数
和a[0]的效果是一样的
随后做p++
这里要提醒大家指针的加和一般数值的加是不一样的
指针的加是带类型的
比如这里,如果p原来是100的话
因为p是整型指针
p+1应该增加一个整型空间的长度
它加了以后呢,应该是104
指向数组中1号下标单元的起始地址
这样通过声明来获得数组存储空间的方法
是静态的方法,在后续课程中
用的更多的是动态的方法
也就是通过malloc申请数组的存储空间
动态申请是在程序执行的过程当中
通过malloc动态申请分配数组存储空间
先看一下定义
首先,定义两个常量
一个是数组初始的大小LIST_INIT_SIZE
这里它定义为20,也就是说
数组初始的时候可以存20个数据
因为数组空间是动态申请的
也给了我们一个优势
就是在数组使用的过程当中
当发现空间不够使用的时候
可以通过重新申请realloc这个函数来获得一个更大的空间
第二个常量就是一个增量
数组初始大小是可以存20个元素
不够了,可以再申请空间
就是在原来空间的基础上增加一个增量的空间
20个不够增加10,获得一个30的空间
如果30不够,那么再增加10,依次类推
用这样的方法,数组的空间是可以变化的
数组被定义成了一个结构体
它包含三个成员
一个是指向整型的指针
因为示例当中我们要存的数据是整数
这个指针会指向申请空间的起始地址
操作中会把它作为一个数组名来使用
另外一个成员listsize是申请的数组空间的容量
也就是数组能存数据的个数
length是当前长度
就是说目前数组中具体存了多少个数据
length小于等于listsize
当等于的时候,说明数组已经存满
再插入新数据的时候就要重新申请空间
用typedef给这个结构体类型取个类型名叫sqlist
后面的数组就是sq list这样类型的
首先声明了一个sqlist结构体的变量L
这就是数组
这个数组本身并没有存储数据的空间
它用来存储数据的空间需要动态申请获得
我们仍然用malloc申请空间
现在要存的数据是整数
用sizeof求其字节数
要存的数据个数LIST_INIT_SIZE
这是数组初始的容量
乘上每个数据需要的字节数
得到整个数组需要空间的大小
malloc返回申请空间的起始地址以后
把它强制类型转换为一个指向整型的指针
赋值给L.elem这个指针
如图所示,这个时候呢
L.elem指向申请空间的起始
那么它是一个指针,就是一个地址
后面就把它看作是一个数组名来使用
目前数组当中没有存储元素
L.length等于0,L.Listsize等于它初始的容量
到这里,我们完成了数组的初始化
为数组准备好了存储空间,这个数组可以使用了
在这样的动态数组当中
数组元素的存取也有两种方式
第1种方式,把指针看做数组名
那么同样用中括号加下标
我们可以存取数组中的某一个元素
例子中是把下标9这个单元的内容赋值给e
第2种方式,可以通过指针来存取数组中的元素
q被赋值L.elem+9
目的是让q指向数组中下标9这个单元的起始地址
用*q把这个单元的内容赋值给e
随后q++,让q指向数组的下一个下标单元
作为动态申请的数组
当不需要这个数组的时候
可以把它的存储空间释放
这时就要用到Free这个函数
free的参数是指向这块待释放空间起始地址的指针
动态申请的空间可以释放
不过只能以成块的方式释放
也就是说申请的时候申请的是这一块
这一块只能一起释放
不能单独释放这块中的某个单元
前面提到动态申请数组空间的另一个优势可以再申请
当使用过程中发现空间不够用的时候
可以通过realloc申请一块更大的空间
realloc包含两个参数
第1个参数是指明原来空间的起始地址
第2个参数和malloc的一样
指明要申请空间的大小
这里空间的大小是通过在原来空间容量的基础上增加一个增量
然后乘以每个数据需要的存储空间大小来给出的
realloc这个函数另外有两个作用
一是把原来空间中的数据拷贝到新空间
二是把原来空间释放
realloc的返回值是新空间的起始地址
新的地址赋值给数组指针L.elem
完成新空间的申请
这个时候,整个数组空间的容量增加了
L.listsize需要重新更新
新空间的容量是在原来容量的基础上增加了一个增量
好了,同学们
我们现在已经讲解完数组和指针
下面我们将讲解指向函数的指针
指针的最后一个问题是指向函数的指针
例子中我们通过int括号*p
括号定义了一个指向函数的指针p
这里p是一个指向函数的指针
int表示这个函数指针是指向返回值为int的函数
如果函数fun是一个返回值为整型的函数
那么我们可以把这个函数名赋值给这个指向函数的指针
让p指向这个函数fun
然后就可以通过p调用函数fun
调用的方式是括号,*p括号
加上相应的参数
这样的调用方式和用函数名直接调用是等价的
当然例子中这样的做法基本上没有什么意义
一般我们是把指向函数的指针定义为函数的形式参数
它是后面章节中二叉树先序遍历的函数
目的是用先序的方式
访问二叉树每个节点一次而且仅一次
它的第1个形式参数指明
要遍历的二叉树T
第2个参数定义了一个指向函数的指针visit
从定义看visit指向返回值为status的函数
当我们要调用该函数的时候
需要把一个函数名作为实参传递给visit这个形参
在函数体中
有通过函数指针调用函数的语句(*visit)
就是说,调用visit所指的函数来处理数据a
从这里可以看出,如果作为实参的函数名不同
函数体中通过visit调用的函数也就不同
比如说有两个返回值类型为status的函数
Printelementher和 addelement
调用Preorder的时候
如果把printelement作为实参传递给visit
那么函数体中用 visit访问函数的时候
访问的函数就是printelement
如果把Addelement作为实参传递给visit
那么函数体中用 visit访问函数的时候
访问的函数就是Addelement
这样组织程序的方式有利于获得良好的程序结构
就是说如果遍历的过程不变
但是,在遍历的过程中
我们对数据的处理方式不一样的话
可以把遍历函数的形式参数定义为指向函数的指针
不同的处理方法用不同的函数实现
当这次遍历要使用某种处理方法的时候
只需要在调用时把对应的函数名
作为实参传递给作为形参的函数指针就可以了
好了,同学们
我们今天的课程就到这里,下节课再见
-1.算法概念导入
-2.数据结构课程介绍
-0.1变量、类型和表达式
-0.2 函数
--0.2 函数
-0.3 指针和单链表
-0.4 数组、指向函数的指针
-1.1什么是数据结构
--测试题
-1.2基本概念和术语
--测试题
-1.3数据结构的描述
--测试题
-1.4抽象数据类型的定义和实现
--测试题
-1.5算法和算法分析概念
--测试题
-1.6算法分析示例
--测试题
-2.1 线性表的类型定义
--测试题
-2.2线性表的顺序表示和实现
--测试题
-2.3 线性链表
--2.3 线性链表
--测试题
-2.4 静态链表
--2.4 静态链表
--测试题
-2.5 循环链表和双向链表
--测试题
-3.1 栈
--3.1栈
--测试题
-3.2 栈的实现
--3.2 栈的实现
--测试题
-3.3 栈的应用
--3.3 栈的应用
-3.4 栈与递归的实现
--测试题
-3.5 队列和链队列
--测试题
-3.6 循环队列
--3.6 循环队列
--测试题
-4.1 串
--4.1 串
--测试题
-5.1 数组定义和表示
--测试题
-5.2矩阵的压缩存储
--测试题
-6.1 树的定义和基本术语
-6.2 二叉树和二叉树的性质
--测试题
-6.3 二叉树的存储结构
--测试题
-6.4 遍历二叉树
--测试题
-6.5 线索二叉树
--测试题
-6.6 树的存储
--6.6树的存储
-6.7 树的转换和遍历
--测试题
-6.8 赫夫曼树
--6.8 赫夫曼树
-6.9 赫夫曼编码
--测试题
-7.1 图的定义和术语
--测试题
-7.2 图的存储结构
--测试题
-7.3 图的遍历
--测试题
-7.4 最小生成树
--测试题
-7.5 有向无环图
--测试题
-7.6 最短路径
--测试题
-8.1 查找基本概念和顺序查找
--测试题
-8.2 有序表的查找
--测试题
-8.3 二叉排序树
--测试题
-8.4 平衡二叉树
--测试题
-8.5 哈希表
--测试题
-9.1插入排序
--测试题
-9.2 希尔排序
--9.2 希尔排序
--测试题
-9.3 快速排序
--9.3 快速排序
--测试题
-9.4 选择排序
--9.4 选择排序
--测试题
-9.5 堆排序
--9.5 堆排序
--测试题
-9.6 归并排序
--9.6 归并排序
--测试题
-9.7 基数排序
--9.7 基数排序
--测试题
-9.8 排序方法总结