信息学奥赛课课通-第8单元 电子课件.ppt_第1页
信息学奥赛课课通-第8单元 电子课件.ppt_第2页
信息学奥赛课课通-第8单元 电子课件.ppt_第3页
信息学奥赛课课通-第8单元 电子课件.ppt_第4页
信息学奥赛课课通-第8单元 电子课件.ppt_第5页
已阅读5页,还剩74页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第8单元指针 作者 林厚从 信息学奥赛课课通 C 第1课指针的概念 学习目标1 理解指针的概念 2 学会定义和使用指针 指针 指针是C 语言的一个重要概念 也是C 语言的重要特色 C 语言的高度灵活性及极强的表达能力 在很大程度上表现在巧妙而灵活的运用指针 通过指针可以有效地表示复杂的数据结构 能够方便地处理数组和字符串 能够动态地分配内存 直接对内存地址进行操作 利用指针作为函数参数 能够实现 一次函数调用 有多个返回值 的目的 因此 必须深入学习和理解指针的概念 体会和掌握指针的各种操作 熟练应用指针去实践编程 1 指针的概念 对于 inta 3 系统会在内存的某个区域开辟连续4个字节的单元存储 对a的操作就是对该内存区域进行操作 至于是具体的哪4个单元 我们并不关心 内存单元的位置 编号 叫作 地址 可以通过取地址操作符 获得一个变量a的起始地址 首个存储单元的地址 a 指针也是一个变量 和普通变量不同的是 指针变量里存储的数据是一个内存地址 就好像一个指示器 指引着你去该内存地址开始的一块内存区域存取数据 2 指针的定义和使用 指针变量的定义格式为 数据类型 指针变量 可以通过赋值语句给指针变量赋值 例如 p a 表示把变量a的内存地址赋值给p 如图8 1 1所示 指针变量初始化 int p NULL 例1 数字变化 问题描述 输入两个不同的整数 把较小的那个数翻倍并输出 输入格式 一行两个整数 int范围以内 之间用一个空格隔开 输出格式 一行一个整数 较小数翻倍后的结果 输入样例 23 输出样例 4 p8 1 1 includeusingnamespacestd intmain inta b int p cin a b if a b p 程序说明 1 变量a和b一旦定义 系统就会给它们分配内存空间 而且在程序运行过程中 其内存地址是固定不变的 这种存储方式称为 静态存储 2 指针变量p定义后 其地址空间是不确定的 默认是NULL 当执行到p a或者p b时 p才指向a或者b的地址 才能确定p的值 这种储存方式称为 动态存储 3 指针的动态性 还体现在可以根据需要 通过函数new 随时申请 看以下例2 例2 阅读程序 写出程序的运行结果 p8 1 2 includeusingnamespacestd intmain int p char q p new int q new char p 65 q p cout p q endl return0 程序说明 1 运行程序 输出结果为 65A 2 程序中声明了两个指针类型 一个指向整数类型的指针p和一个指向字符类型的指针q 分别通过new int 和new char 为p和q向内存申请空间 3 p 65 把65这个整数存放到p所指向的内存单元 4 q p 把p所指向的内存单元里的值赋值给q所指向的内存单元 由于q指向的是一个字符类型 而p指向的是一个整数类型 在赋值的时候执行了类型的强制转换 最终q所指向的内存单元里存储的是65号字符即为A 3 指针的理解 1 指针的类型 从语法角度看 把指针定义语句里的指针名字去掉 剩下的部分就是这个指针的类型 也就是指针本身所具有的类型 int ptr 指针的类型是int char ptr 指针的类型是char int ptr 指针的类型是int int ptr 3 指针的类型是int 3 int ptr 4 指针的类型是int 4 2 指针所指向的类型 当通过指针来访问指针所指向的内存区域时 指针所指向的类型决定了编译器将把那片内存区域里的内容当作什么来看待 从语法上看 把指针定义语句中的指针名字和名字左边的指针声明符 去掉 剩下的就是指针所指向的类型 int ptr 指针所指向的类型是intchar ptr 指针所指向的类型是charint ptr 指针所指向的类型是int int ptr 3 指针所指向的类型是int 3 int ptr 4 指针所指向的类型是int 4 在指针的算术运算中 指针所指向的类型有很大的作用 指针的类型和指针所指向的类型是两个不同的概念 3 指针的值 指针的值是指针本身存储的数值 这个值将被编译器当作一个地址 而不是一般的数值 在位长32位的系统中 内存地址都是32位的 所以所有类型的指针的值都是一个32位整数 指针所指向的内存区域就是从指针的值所代表的那个内存地址开始 长度为sizeof 指针所指向的类型 的一片内存区域 我们说一个指针的值是X 就相当于说该指针指向了以X为首地址的一片内存区域 指针所指向的内存区域和指针所指向的类型是两个完全不同的概念 4 指针本身所占据的内存区 用函数sizeof 指针的类型 测一下就知道了 在32位的系统中 指针本身占据了4字节的长度 例3 阅读程序 写出程序的运行结果 p8 1 3 includeusingnamespacestd intmain int p q p int malloc 40 动态申请40字节用来存放int类型 并返回首地址给pq p p 1 p p 2 free q 释放刚才申请的40字节的空间return0 程序说明 1 单步跟踪程序 发现执行完 p 1 q和p为同一个地址 p和 q的值也都是1 2 继续跟踪程序 执行完 p 2 p指向了下一个整数空间 在原来的地址上加4 因为一个整型占4个字节 p的值也变成了2 3 程序中的 free q 是释放一开始申请的40个字节的内存空间 是配合malloc 使用 注意释放的是内存 q和p的地址还在 指针并没有被释放 指针仍然指向原来的存储空间 指针是一个变量 只有程序结束时才被销毁 释放了内存空间后 原来指向这块空间的指针还是存在 只不过现在指针指向的内容是未定义的 里面可能会有一些垃圾内容 继续跟踪看看p q p q的值 动态申请内存 1 malloc void malloc unsignedsize 在内存的动态存储区中分配一块长度为size字节的连续区域 参数size为需要内存空间的长度 返回该区域的首地址 2 calloc void calloc size tnumelements size tsizeofelement 与malloc相似 参数sizeofelement为申请地址的单位元素长度 numelements为元素个数 即在内存中申请numelements sizeofelement字节大小的连续地址空间 3 realloc void realloc void ptr unsignednewsize 给一个已经分配了地址的指针重新分配空间 参数ptr为原有的空间地址 newsize是重新申请的地址长度 实践巩固 第2课指针的引用与运算 学习目标1 理解并学会引用指针 2 掌握指针的常用运算 1 指针的引用 首先理解指针变量与普通变量的区别和对应关系 例如 定义一个指针变量 int p 和一个普通变量 inta 关于两者之间的各种引用方式对应关系如下 1 p 等同于 a 表示的是内存地址 2 p 等同于 a 表示变量里存储的实际数据 3 p 3 等同于 a 3 表示变量的赋值方法 2 指针的运算 如果定义的是局部指针变量 其地址就是随机的 直接操作会引发不可预测的错误 所以 指针变量一定要初始化后才能引用 由于指针变量存储的是内存地址 所以也可以执行加法 减法运算 一般用来配合数组进行寻址操作 例如 例1 阅读并上机调试以下程序 体会指针变量的加法运算 p8 2 1 includeusingnamespacestd intmain inta 100 n cin n for inti 0 i a i int p 问题分析 1 程序的作用是输入n及n个整数 使用指针变量依次遍历输出 2 程序中的 p 是广义的 p p 1 本质上是 p sizeof int 3 注意 p 3 和 p 3 是不同的 对于本题 前者是指a 0 3 而后者是指a 3 例2 求和 问题描述 输入n个正整数 要求对这n个数中的奇数和偶数分别求和 输入格式 第1行1个正整数n 1 n 5000 以下n行 每行一个正整数 1 20000之间 输出格式 2行2个整数 第1行为所有奇数之和 第2行为所有偶数之和 输入样例 5310758 输出样例 1518 p8 2 2 includeusingnamespacestd intmain intn a 5011 int p s1 s2 s1和s2指向的单元分别存放偶数和 奇数和cin n for inti 0 i a i p 例3 阅读并上机调试以下程序 体会无类型指针的使用 p8 2 3 includeusingnamespacestd intmain inta 10 doubleb 3 5 void p p 例4 阅读并上机调试以下程序 体会多重指针的使用 p8 2 4 includeusingnamespacestd intmain inta 10 int p int pp p 实践巩固 第3课指针与数组 学习目标1 理解数组指针 2 学会使用指针实现数组操作 3 学会使用指针实现字符串操作 指针与数组 在C 中 数组名在一定意义上可以被看成指针 数组的指针 是指整个数组在内存中的起始地址 数组元素的指针 是指数组中某个元素所占存储单元的地址 一般可以使用 下标法 访问数组元素 如a 5 也可以使用 地址法 访问数组元素 因为数组名就代表数组在内存中的起始地址 也就是a 0 的地址 如a 4就表示a 4 的地址 也可以通过 指针法 访问数组元素 通过数组的指针或者数组元素的指针访问数组元素 能使目标程序质量更高 占用内存更少 运行速度更快 例1 阅读并上机调试以下程序 体会数组的指针和数组元素的指针 p8 3 1 includeusingnamespacestd intmain inta 10 11 12 13 14 15 int p a 4 cout a cout a 3 cout p endl return0 问题分析 1 运行程序 输出 101315 2 程序中直接拿数组名a当指针用 但是a始终是静态的 是不可变的 不能做 a a 4 运算 而指针可以做 p 或 p p 4 运算 3 语句 scanf d n 其实就是指针的意思 如果是数组 就不需要加取地址符 例2 阅读并上机调试以下程序 体会动态数组的定义和使用 p8 3 2 includeusingnamespacestd intmain intn a cin n a newint n 申请n个连续的int类型内存空间 动态数组 并返回首地址给afor inti 0 i a i for inti 1 i n i a i a i 1 for inti 0 i n 1 i cout a i cout a n 1 endl return0 问题分析 1 运行程序 输入10及如下10个数 12345678910 输出 13610152128364555 2 因为指针可以动态申请空间 那一次申请100个变量空间 系统给的地址是连续的 就可以当成数组使用 这就是 动态数组 的一种 3 在信息学竞赛中遇到大批量数据的情况下 数组开小只能拿部分分 开大又可能爆空间 此时就可以定义和使用动态数组 例3 行列转换 问题描述 对于一个n m的稀疏矩阵 按照行 列 值的格式读入k个元素 其他位置的值为0 再输出这些数 输入格式 第1行3个整数 表示n m和k 每两个数之间用一个空格隔开 以下k行 按照 行优先 从上到下 从左到右 的方式读入k个非0元素 每行3个数 依次为行号 列号 元素值 每两个数之间用一个空格隔开 输出格式 输出k个数 按照 列优先 从左到右 从上到下 的方式输出 每两个数之间用一个空格隔开 输入样例 453121214234345 输出样例 124523 例4 阅读并上机调试以下程序 体会使用指针实现字符串的输入输出及存储 p8 3 4 include includeusingnamespacestd intmain char s s newchar 给s申请一个地址cin s 输入字符串以空格或者回车结束cout s endl cout strlen s return0 程序说明 运行程序 输入 helloworld 输出 hello5使用指针实现字符串的输入 输出及存储 与普通数组类似 只是在使用前一定要给字符串变量指定一个地址 例5 阅读并上机调试以下程序 体会使用指针实现字符串的复制 p8 3 5 includeusingnamespacestd voidcopy string char from char to 用字符指针作为函数参数while from 0 to from to from to 0 在结束位置强制加结束符 intmain chara 20 clanguage charb 20 verygood copy string a b cout a endl cout b endl return0 实践巩固 第4课函数指针及扩展 学习目标1 理解并学会使用函数指针 2 了解函数指针数组 3 了解引用与指针的区别 4 了解指向结构体的指针 函数指针及扩展 程序中需要处理的数据都保存在内存空间 而程序以及函数同样也保存在内存空间 C 支持通过函数的入口地址 指针 访问函数 另一方面 有些函数在编写时要调用其他的辅助函数 但是尚未确定 在具体执行时 再为其传递辅助函数的地址 比如排序函数sort a a n cmp 其中的比较函数cmp是根据需要传递给sort的 就是传递了一个函数指针 函数指针就是指向函数的指针变量 定义格式如下 类型名 函数名 参数 例1 阅读并上机调试以下程序 体会函数指针的使用 p8 4 1a includeusingnamespacestd inttest inta returna a intmain cout test endl 或者 使用函数指针需要注意的几点 1 定义函数指针要与函数原型一致 例如 函数为 inttest int 则函数指针声明为 int fp int 2 获取函数的地址有两种方式 一种是直接使用函数名 如test或者fp test 另一种是使用取地址符 如 test或者fp test 3 调用函数有两种方式 一种是直接使用函数名 如fp 5 另一种是使用函数指针调用函数 如 fp 5 4 函数指针还支持一种结合typedef的定义方式 参见下一页例1的另一个程序 5 可以定义一个数组存放多个函数指针 参见例2 p8 4 1b includeusingnamespacestd intadd inta intb returna b typedefint addp int int 声明一个函数指针变量addpintmain addpfp add 定义addp类型的函数指针fp 并赋值为addcout fp 2 3 endl 程序输出5return0 例2 模拟计算器 问题描述 输入两个正整数m和n 再输入一个代表运算方案的数字k 1代表求m n的值 2代表求m n的值 3代表求m n的值 4代表求m n的值 整除 请函数指针数组编程模拟计算器 输出相应的运算结果 输入格式 第1行为2个正整数m和n 第2行为1个正整数k 1 k 4 输出格式 一行一个整数 表示相应的运算结果 输入样例 681 输出样例 14 p8 4 2 include includeusingnamespacestd intcal1 inta intb returna b intcal2 inta intb returna b intcal3 inta intb returna b intcal4 inta intb returna b typedefint f inta intb 自定义一个函数指针变量类型fintmain freopen cal in r stdin freopen cal out w stdout intm n k fa 4 cal1 cal2 cal3 cal4 定义函数指针数组a 4个元素 每个元素为f类型 并且分别赋初值cin m n k cout a k 1 m n endl 用a k 1 来调用相应的函数return0 例3 阅读并上机调试以下程序 体会引用变量的使用 p8 4 3 includeusingnamespacestd intmain inta 10 b 20 int f int 问题分析 1 运行程序 输出 a 10a 152 引用变量 是C 中的一种复合类型 它的本质就是给原变量起了一个别名 就像生活中有个人叫 张三 大家给他取了一个别名叫 乐乐 那么这两个名字指的是同一个人 引用就相当于给原变量取了一个别名 对引用变量的操作就是对原变量的操作 3 注意区分引用变量与指针变量 引用也不是取地址 另外 引用在定义的同时必须进行初始化 不能通过赋值语句 把对一个变量的引用改成对另一个变量的引用 4 程序中的a f和ra都是指向同一个内存地址 5 使用引用会使程序代码更加简洁 一般用在多重数组中 例如递推式 f a i j f a i j 1 f a i j 2 a i j 可以先定义引用变量 int k a i j 然后简化写成 f k f k 1 f k 2 k 6 函数参数一般都是使用普通变量实现 传值 方式 也可以使用指针参数实现 传址 方式 比如数组 有了引用 就可以使用 引用参数 使得函数中的变量名成为调用函数中的变量的别名 这种传递参数的方式叫 按引用传递 例4 阅读并上机调试以下程序 体会引用参数 p8 4 4 交换两个变量的值 includeusingnamespacestd voidchange1 int voidchange3 intm intn 普通变量参数intt m m n n t return intmain intx 7 y 160 change1 x y cout x y endl change2 例5 阅读并上机调试以下程序 体会指向结构体变量的指针 p8 4 5 includeusingnamespacestd structtstudent intnum charname 20 charsex intage tstudentx 13 lihao m 35 tstudent p intmain p 程序说明 1 运行程序 输出 13Lihao352 一个指向结构体变量的指针就是该变量占据的内存空间的起始地址 如图8 4 1所示 下一页 3 访问结构体成员的方法为 结构体名 成员名 使用指针访问结构体成员有两种方式 结构体名 成员名 或者 指针变量名 成员名 例如p name 或者 p name 但不能写成 p name 4 如果函数的参数是一个结构体变量 按值传递 那么函数调用时就要复制整个结构体 效率不高 这种情况下 一般使用指针参数或者引用参数 实践巩固 第5课指针应用举例 学习目标学会使用指针建立链表 并熟练应用链表结构解决一些实际问题 线性表是一种常用的数据结构 其中的每一个元素 结点 都有唯一的前驱和唯一的后续 当然 第一个元素只有后续 最后一个元素只有前驱 线性表一般分为 顺序表 和 链表 顺序表可以简单理解成前面学过的数组 它是一种逻辑上和物理上都是有序的 连续存储的静态结构 链表是一种物理上不连续存储的动态结构 数据之间的逻辑顺序是通过链表中的指针链接关系实现的 线性表 链表 链表由一系列 结点 组成 结点可以在需要时动态生成 每个结点的数据域至少包括两部分 一是存储数据元素的 数据域 二是存储下一个结点内存地址的 指针域 图8 5 1就是一个链表的示意图 其中 head表示头结点 数据域的值为5的结点为尾结点 其指针域为NULL 链表中每个结点的数据结构定义如下 structnode intnum node next 1 链表的建立 建立链表有 头插法 和 尾插法 两种方法 前者是把新结点插在头结点之后 后者是把新结点插在尾结点之后 本课以尾插法为例建立链表 就是不断地在链表尾部链接一个新结点 如图8 5 2所示 r为链表的尾部结点 p为要增加的新结点 只需要把r next指向p即可 然后把r再指向新的尾部 1 链表的建立 尾插法 2 链表的遍历 链表的遍历就是从链表的头结点head开始 依次访问每一个元素 直到链表尾部 包括查找 输出等操作 如果已经创建了一个链表 如何查找其中是否有给定的元素x呢 只需要从头结点开始扫描 如果当前结点数据域的值等于x 那么就表示找到了x 如果到达链表尾部还没有找到 则链表中没有x这个元素 同样 链表的输出也是从头结点开始一个一个输出 直到链表尾部结束 3 链表的插入 链表的插入就是在链表给定位置插入一个新结点 如图8 5 3所示 就是在链表数据域值为4和9的两个结点中间插入一个数据域值为7的结点 如果在表头表尾插入结点要特殊处理 4 链表的删除 链表的删除就是删除链表中的某一个元素 如图8 5 4所示 就是要删除链表中数据域值为7的结点 如果删除头结点要特殊处理 例1 陶陶摘苹果 问题描述 陶陶家的院子里有一棵苹果树 秋天树上会结10个苹果 苹果成熟的时候 陶陶就会跑去摘苹果 陶陶有个30厘米高的板凳 当她不能直接用手摘到苹果的时候 就会踩到板凳上再试试 现在已知10个苹果到地面的高度 以及陶陶把手伸直的时候能够达到的最大高度 请帮陶陶算一下她能够摘到的苹果的数目 假设她碰到苹果 苹果就会掉下来 输入格式 第1行包含10个100 200之间 包括100和200 的正整数 以厘米为单位 分别表示10个苹果到地面的高度 两个相邻的正整数之间用一个空格隔开 第2行只包括一个100 120之间 包含100和120 的正整数 以厘米为单位 表示陶陶把手伸直的时候能够达到的最大高度 输出格式 一行一个整数 表示陶陶能够摘到的苹果的数目 输入样例 100200150140129134167198200111110 输出样例 5 问题分析 定义一个一维数组记录10个苹果的高度 如果陶陶手伸直能够到的高度加上30大于或等于苹果的高度 那么这个苹果就会掉下来 则能摘到的苹果数就加一 可以用动态存储实现本题 p8 5 1 include includeusingnamespacestd inta 11 intmain freopen apple in r stdin freopen apple out w stdout int p ints 0 p a for inti 0 i p p p new int cin p for inti 0 i 10 i if a i p 30 s cout s endl return0 建议跟踪程序 体会过程 例2 插入排序 问题描述 给出一个整数x和一个数列 这个数列保证从小到大排列 现在要求将这个整数x插入到数列中 使新的数列仍然从小到大排列 输入格式 第1行1个整数n 表示数列中数的个数 第2行n个整数 之间用一个空格隔开 保证从小到大 第3行1个整数x 表示等待插入的整数 输出格式 一行整数 表示新的数列 每两个数之间用一个空格隔开 输入样例 413452 输出样例 12345 问题分析 由于题目中并没有明确n的范围 所以定义一个动态链表 从头结点开始扫描链表 找到第一个大于或等于要插入数字的位置 就把要插入的数字插在这个位置的前面 如果没有找到比插入数大或者与其相等的 则插在链表的尾部 具体程序和说明参见

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论