Chapter 17: Advanced Uses of Pointers Copyright © 2008 W. W. Norton & Company. All rights reserved. 1 Chapter 17 指针的高级用法.

Slides:



Advertisements
Similar presentations
一、统计范围 注册地在湖里区的具有房地产开发资质的 房地产开发企业 无论目前是否有开发项目 无论开发的项目是在湖里区还是在其他区 没有开发项目的企业需要报送年报和月报 中的资金表(空表)。 新成立的项目公司,要先入库,再报报表。
Advertisements

第十二章 常微分方程 返回. 一、主要内容 基本概念 一阶方程 类 型 1. 直接积分法 2. 可分离变量 3. 齐次方程 4. 可化为齐次 方程 5. 全微分方程 6. 线性方程 类 型 1. 直接积分法 2. 可分离变量 3. 齐次方程 4. 可化为齐次 方程 5. 全微分方程 6. 线性方程.
退 出退 出退 出退 出 上一页 下一页 仪器使用 §1-2 尺规绘图工具和仪器的使用方法 图板丁字尺三角板 比例尺圆规分规铅笔曲线板。 要提高绘图的准确度和绘图效率,必须正确地使用各种绘图工 具和仪器。常用的手工绘图工具和仪器有图板、丁字尺、三角板、 比例尺、圆规、分规、铅笔、曲线板等。 提示:将光标放在仪器上,
Advanced Uses of Pointers
Programming III SPRING 2015 School of Computer and Information Sciences Francisco R. Ortega, Ph.D. McKnight Fellow and GAANN Fellow LECTURE #5 More about.
概率统计( ZYH ) 节目录 3.1 二维随机变量的概率分布 3.2 边缘分布 3.4 随机变量的独立性 第三章 随机向量及其分布 3.3 条件分布.
基本知识和几何要素的投影 模块一: 字体练习 第一章 制图的基本知识与基本技能 题目提示返回.
第 12 章位运算 C 语言兼具高级语言及低级语言的特性,因此 适合编写系统软件。 C 语言具备低级语言的特性 就在于它能直接对硬件进行操作,即位运算。 所谓位运算是指,按二进制位进行的运算。 例如,将一个存储单元中各二进位左移或右移一 位等。
位置相关查询处理 研究背景及意义 移动计算、无线通信以及定位技术的快速发展,使 得位置相关的查询处理及基于位置的信息服务技术 已经成为一个热点研究领域 。 大量的应用领域 ( 如地理信息系统、智能导航、交 通管制、天气预报、军事、移动电子商务等 ) 均迫 切需要有效地查询这些数据对象。
细分曲面 傅孝明 SA 目录 细分曲面的基本思想 两个关键问题 一些基本概念 几种简单的细分曲面算法 细分曲面方法分类.
计算机 在分析化学的应用 ( 简介 ) 陈辉宏. 一. 概述 信息时代的来临, 各门学科的研究方法都 有了新的发展. 计算机的介入, 为分析化学的进展提供了 一种更方便的研究方法.
编译程序 构造原理和实现技术 授课教师:吕江花. 第一章 编译程序概述 主要内容: 几个基本概念 编译器的工作过程概述 编译器各个阶段的功能描述 编译程序的实现途径.
嵌入式操作系统 陈香兰 Fall 系统调用 10/27/09 嵌入式 OS 3/12 系统调用的意义  操作系统为用户态进程与硬件设备进行交互提供 了一组接口 —— 系统调用  把用户从底层的硬件编程中解放出来  极大的提高了系统的安全性  使用户程序具有可移植性.
第 4 章 抽象解释 内容概述 以一种独立于编程语言的方式,介绍抽象解释的 一些本质概念 – 将 “ 程序分析对语言语义是正确的 ” 这个概念公式 化 – 用 “ 加宽和收缩技术 ” 来获得最小不动点的较好的 近似,并使所需计算步数得到限制 – 用 “ 伽罗瓦连接和伽罗瓦插入 ” 来把代价较大的属 性空间用代价较小的属性空间来代替.
吉林大学远程教育课件 主讲人 : 杨凤杰学 时: 64 ( 第六十二讲 ) 离散数学. 最后,我们构造能识别 A 的 Kleene 闭包 A* 的自动机 M A* =(S A* , I , f A* , s A* , F A* ) , 令 S A* 包括所有的 S A 的状态以及一个 附加的状态 s.
1 为了更好的揭示随机现象的规律性并 利用数学工具描述其规律, 有必要引入随 机变量来描述随机试验的不同结果 例 电话总机某段时间内接到的电话次数, 可用一个变量 X 来描述 例 检测一件产品可能出现的两个结果, 也可以用一个变量来描述 第五章 随机变量及其分布函数.
计算机文化基础 第 13 章 多表操作. 多表操作 以前所进行的操作中,在同一时刻只能打开一个表文 件,这是单工作区操作。但是在有些情况下,我们需要同时 了解多个表文件中的内容,例如 图 8-1 。在表文件 Stud1.DBF 中,有姓名,班级,电话三项;在 Stud2.DBF 中, 有姓名,性别,籍贯,英语四个字段。在单工作区操作方式.
数 学 系 University of Science and Technology of China DEPARTMENT OF MATHEMATICS 第 3 章 曲线拟合的最小二乘法 给出一组离散点,确定一个函数逼近原函数,插值是这样 的一种手段。在实际中,数据不可避免的会有误差,插值函 数会将这些误差也包括在内。
主讲教师:陈殿友 总课时: 124 第八讲 函数的极限. 第一章 机动 目录 上页 下页 返回 结束 § 3 函数的极限 在上一节我们学习数列的极限,数列 {x n } 可看作自变量 为 n 的函数: x n =f(n),n ∈ N +, 所以,数列 {x n } 的极限为 a, 就是 当自变量 n.
吉林大学远程教育课件 主讲人 : 杨凤杰学 时: 64 ( 第三十八讲 ) 离散数学. 第八章 格与布尔代数 §8.1 引 言 在第一章中我们介绍了关于集 合的理论。如果将 ρ ( S )看做 是集合 S 的所有子集组成的集合, 于是, ρ ( S )中两个集合的并 集 A ∪ B ,两个集合的交集.
吉林大学远程教育课件 主讲人 : 杨凤杰学 时: 64 ( 第四十八讲 ) 离散数学. 例 设 S 是一个非空集合, ρ ( s )是 S 的幂集合。 不难证明 :(ρ(S),∩, ∪,ˉ, ,S) 是一个布尔代数。 其中: A∩B 表示 A , B 的交集; A ∪ B 表示 A ,
第十一章 曲线回归 第一节 曲线的类型与特点 第二节 曲线方程的配置 第三节 多项式回归.
线性代数习题课 吉林大学 术洪亮 第一讲 行 列 式 前面我们已经学习了关 于行列式的概念和一些基本 理论,其主要内容可概括为:
吉林大学远程教育课件 主讲人 : 杨凤杰学 时: 64 ( 第二十五讲 ) 离散数学. 定理 群定义中的条件 ( 1 )和( 2 )可以减弱如下: ( 1 ) ’ G 中有一个元素左壹适合 1 · a=a; ( 2 ) ’ 对于任意 a ,有一个元素左逆 a -1 适 合 a -1 ·
第 4 章 过程与变量的作用范围. 4.1 Visual Basic 的代码模块 Visual Basic 的应用程序是由过程组成的, 过程代码存放在模块中。 Visual Basic 提供了 三类模块,它们是窗体模块、标准模块和类 模块。 窗体模块 窗体模块是大多数 Visual Basic.
数 学 系 University of Science and Technology of China DEPARTMENT OF MATHEMATICS 第 3 章 曲线拟合的最小二乘法 给出一组离散点,确定一个函数逼近原函数,插值是这样的一种手段。 在实际中,数据不可避免的会有误差,插值函数会将这些误差也包括在内。
自顶向下分析 —— 递归下降法 递归下降法 (Recursive-Descent Parsing) 对每个非终极符按其产生式结构产生相应语 法分析子程序. 终极符产生匹配命令 非终极符则产生调用命令 文法递归相应子程序也递归,所以称这种方 法为递归子程序方法或递归下降法。
OS 进程调度模拟演示 制作人: 钱晶 高上上. OS 进程调度模拟-实验原理 静态优先级原理 在这种方式下,系统一旦把处理机分配给就绪队 列中的优先权最高的进程后,该进程便一直执行下去, 直至完成。或因为发生某事件使该进程放弃处理机,系 统方可再将处理机分配给另一优先级最高的进程。这些 事件包括有优先级更高的进程进入,或是因为某些原因.
非均相物系的分离 沉降速度 球形颗粒的 :一、自由沉降 二、沉降速度的计算 三、直径计算 1. 试差法 2. 摩擦数群法 四、非球形颗粒的自由沉降 1. 当量直径 de :与颗粒体积相等的圆球直径 V P — 颗粒的实际体积 2. 球形度  s : S—— 与颗粒实际体积相等的球形表面积.
量子化学 第四章 角动量与自旋 (Angular momentum and spin) 4.1 动量算符 4.2 角动量阶梯算符方法
数 学 系 University of Science and Technology of China DEPARTMENT OF MATHEMATICS 第 5 章 解线性方程组的直接法 实际中,存在大量的解线性方程组的问题。很多数值方 法到最后也会涉及到线性方程组的求解问题:如样条插值的 M 和.
主讲教师:陈殿友 总课时: 124 第十一讲 极限的运算法则. 第一章 二、 极限的四则运算法则 三、 复合函数的极限运算法则 一 、无穷小运算法则 机动 目录 上页 下页 返回 结束 §5 极限运算法则.
在发明中学习 线性代数 概念的引入 李尚志 中国科学技术大学. 随风潜入夜 : 知识的引入 之一、线性方程组的解法 加减消去法  方程的线性组合  原方程组的解是新方程的解 是否有 “ 增根 ” ?  互为线性组合 : 等价变形  初等变换  高斯消去法.
东南大学计算中心 网站应用与实践 主讲人 吴俊. 2 东南大学计算中心 网站制作流程  确定主题、风格  规划栏目、收集素材  版面设计、配色  编辑页面  测试发布 FrontPage 要完成的任务.
Photoshop CS4 标准培训教程 第三章第三章 在 Photoshop CS4 中所谓的不规则选区指的是随意性强,不被局限在几何形状内, 他们可以是鼠标任意创建的也可以是通过计算而得到的单个选区或多个选区。在 Photoshop 中可以用来创建不规则选区的工具被分组放置到套索工具组、魔棒工具组.
第一节 相图基本知识 1 三元相图的主要特点 (1)是立体图形,主要由曲面构成; (2)可发生四相平衡转变; (3)一、二、三相区为一空间。
9的乘法口诀 1 .把口诀说完全。 二八( ) 四六( ) 五八( ) 六八( ) 三七( ) 三八( ) 六七( ) 五七( ) 五六( ) 十六 四十八 四十二 二十四 二十一 三十五 四十 二十四 三十 2 .口算, 并说出用的是哪句口诀。 8×8= 4×6= 7×5= 6×8= 5×8=
第 3 章 控制流分析 内容概述 – 定义一个函数式编程语言,变量可以指称函数 – 以 dynamic dispatch problem 为例(作为参数的 函数被调用时,究竟执行的是哪个函数) – 规范该控制流分析问题,定义什么是可接受的控 制流分析 – 定义可接受分析在语义模型上的可靠性 – 讨论分析算法.
吉林大学远程教育课件 主讲人 : 杨凤杰学 时: 64 ( 第五十三讲 ) 离散数学. 定义 设 G= ( V , T , S , P ) 是一个语法结构,由 G 产生的语言 (或者说 G 的语言)是由初始状态 S 演绎出来的所有终止符的集合, 记为 L ( G ) ={w  T *
编译原理总结. 基本概念  编译器 、解释器  编译过程 、各过程的功能  编译器在程序执行过程中的作用  编译器的实现途径.
周期信号的傅里叶变换. 典型非周期信号 ( 如指数信号, 矩形信号等 ) 都是满足绝对可 积(或绝对可和)条件的能量信号,其傅里叶变换都存在, 但绝对可积(或绝对可和)条件仅是充分条件, 而不是必 要条件。引入了广义函数的概念,在允许傅里叶变换采用 冲激函数的前提下, 使许多并不满足绝对可积条件的功率.
 符号表  标识符的作用: 声明部分:定义了各种对象及对应的属性和 使用规则。 程序体:对所定义的对象进行各种操作。 $ididname IdnameAttributeIR  必要性 Token : 新表-符号表(种类、类型等信息):
Department of Mathematics 第二章 解析函数 第一节 解析函数的概念 与 C-R 条件 第二节 初等解析函数 第三节 初等多值函数.
1 第 7 章 存储过程、触发器和程序包 在很多时候,都需要保存 PL/SQL 程序块,以便 随后可以重新使用。这也意味着,程序块需要一个名 称,这样需才可以调用或者引用它。命名的 PL/SQL 程序块可被独立编译并存储在数据库中,任何与数据 库相连接的应用程序都可以访问这些存储的 PL/SQL 程序块。
网上预约集港操作指南 一、登录系统 登陆下面图片显示网址:输入堆场用户名、密码和校验码登陆系统.
首 页 首 页 上一页 下一页 本讲内容本讲内容 视图,剖视图(Ⅰ) 复习: P107 ~ P115 作业: P48(6-2,6-4), P49( 去 6-6) P50, P51(6-13), P52 P50, P51(6-13), P52 P53 (6-18,6-20) P53 (6-18,6-20)
《 UML 分析与设计》 交互概述图 授课人:唐一韬. 知 识 图 谱知 识 图 谱知 识 图 谱知 识 图 谱.
1 、如果 x + 5 > 4 ,那么两边都 可得 x >- 1 2 、在- 3y >- 4 的两边都乘以 7 可得 3 、在不等式 — x≤5 的两边都乘以- 1 可得 4 、将- 7x — 6 < 8 移项可得 。 5 、将 5 + a >- 2 a 移项可得 。 6 、将- 8x < 0.
名探柯南在侦查一个特大盗窃集团过程 中,获得藏有宝物的密码箱,密码究竟 是什么呢?请看信息: ABCDEF( 每个字 母表示一个数字 ) A :是所有自然数的因数 B :既有因数 5 ,又是 5 的倍数 C :既是偶数又是质数 D :既是奇数又是合数 EF :是 2 、 3 、 5 的最小公倍数.
项目七: PLC 功能指令应用 带进位循环左移指令 XXXXX. 项目七: PLC 功能指令应用 FX2 系列可编程控制器移位控制指令有移位、循环移位、字移位 及先进先出 FIFO 指令等 10 条指令。 带进位循环右移指令 RCR 带进位循环左移指令 RCL 字右移位指令 WSFR 先入先出读出指令.
本讲主要内容: 1. 如何登陆网站后台 2. 基础信息修改 3. 网站文章类型资料更新 4. 网站图片类型资料更新 5. 网站链接类型资料更新 本讲主要目的: 在结束本讲内容之后,能够按照客户的需求对网站的资料 进行实时更新操作。
§10.2 对偶空间 一、对偶空间与对偶基 二、对偶空间的有关结果 三、例题讲析.
表单自定义 “ 表单自定义 ” 功能是用于制作表单的 工具,用数飞 OA 提供的表单自定义 功能能够快速制作出内容丰富、格 式规范、美观的表单。
7 生产费用在完工产品与在产 品之间分配的核算. 2 第七章 生产费用在完工产品与在产品之 间的分配  知识点 :  理解在产品的概念  掌握生产费用在完工产品与在产品之间的分 配.
力的合成 力的合成 一、力的合成 二、力的平行四边形 上一页下一页 目 录 退 出. 一、力的合成 O. O. 1. 合力与分力 我们常常用 一个力来代替几个力。如果这个 力单独作用在物体上的效果与原 来几个力共同作用在物体上的效 果完全一样,那么,这一个力就 叫做那几个力的合力,而那几个 力就是这个力的分力。
个体 精子 卵细胞 父亲 受精卵 母亲 人类生活史 问题:人类产生配子(精、卵 细胞)是不是有丝分裂?
逻辑设计基础 1 第 7 章 多级与(或)非门电路 逻辑设计基础 多级门电路.
八. 真核生物的转录 ㈠ 特点 ① 转录单元为单顺反子( single cistron ),每 个蛋白质基因都有自身的启动子,从而造成在功能 上相关而又独立的基因之间具有更复杂的调控系统。 ② RNA 聚合酶的高度分工,由 3 种不同的酶催化转 录不同的 RNA 。 ③ 需要基本转录因子与转录调控因子的参与,这.
一、 版 面 构 成 的 概 念 版 面 构 成 的 概 念 二、 版 面 构 成 的 发 展 趋 势 版 面 构 成 的 发 展 趋 势 三、 广 告 文 字 的 版 面 构 成 广 告 文 字 的 版 面 构 成 四、 广 告 版 面 的 视 觉 流 程 广 告 版 面 的 视 觉 流 程.
欢 迎 使 用 《工程流体力学》 多媒体授课系统 燕 山 大 学 《工程流体力学》课程组. 第九章 缝隙流动 概述 9.1 两固定平板间的层流流动 9.2 具有相对运动的两平行平板 间的缝隙流动 9.3 环形缝隙中的层流流动.
Word 绘制流程图 图解图解图解图解. 第一步:绘制对象 打开一个 Word 空白文档。 在 “ 绘图 ” 工具栏上,单击 “ 自选图形 ” ,指向 “ 流程图 ” , 再单击所需的形状。 图解.
1 第三章 数列 数列的概念 考点 搜索 ●数列的概念 ●数列通项公式的求解方法 ●用函数的观点理解数列 高考 猜想 以递推数列、新情境下的 数列为载体, 重点考查数列的通 项及性质, 是近年来高考的热点, 也是考题难点之所在.
第二节. 广告牌为什么会被风吹倒? 结构的稳定性: 指结构在负载的作用下 维持其原有平衡状态的能力。 它是结构的重要性质之一。
§9. 恒定电流场 第一章 静电场 恒定电流场. 电流强度  电流:电荷的定向移动  正负电荷反方向运动产生的电磁效应相同 ( 霍尔效应 特例 ) 规定正电荷流动的方向为正方向  电流方向:正方向、反方向  电流强度 ( 电流 ) A 安培 标量 单位时间通过某一截面的电荷.
第二章 线性表 第二节 线性链表 5. 线性表的链式表示 顺序表的优点是可以随机选取表中元素 缺点是插入删除操作复杂。 用指针将互不相连的内存结点串成的 线性表叫线性链表。 结点 node 由一个数据元素域,一个或几个 指针域组成。单链表的结点只有一个指针域。
目录 上页 下页 返回 结束 二、无界函数反常积分的审敛法 * 第五节 反常积分 无穷限的反常积分 无界函数的反常积分 一、无穷限反常积分的审敛法 反常积分的审敛法  函数 第五章 第五章.
本章讨论有限自由度结构系统,在给定载荷和初始条件激励下的系统动力响应计算方法。 第 六 章
Chapter 17: Advanced Uses of Pointers Copyright © 2008 W. W. Norton & Company. All rights reserved. 1 Chapter 17 Advanced Uses of Pointers.
§7.2 估计量的评价标准 上一节我们看到,对于总体 X 的同一个 未知参数,由于采用的估计方法不同,可 能会产生多个不同的估计量.这就提出一 个问题,当总体的一个参数存在不同的估 计量时,究竟采用哪一个好呢?或者说怎 样评价一个估计量的统计性能呢?下面给 出几个常用的评价准则. 一.无偏性.
Advanced Uses of Pointers
Presentation transcript:

Chapter 17: Advanced Uses of Pointers Copyright © 2008 W. W. Norton & Company. All rights reserved. 1 Chapter 17 指针的高级用法

Chapter 17: 指针高级用法 动态存储分配 C’ 语言的数据结构,包括数组,其大小通常都 是固定的. 大小固定的数据结构存在问题, 因为我们写程 序时将被迫面临选择他们的大小. 幸运的是, C 支持动态存储分配 : 根据程序的执 行来分配存储器的能力. 使用动态存储分配, 我们能够设计数据结构使 其按需增加或减少. Copyright © 2008 W. W. Norton & Company. All rights reserved. 2

Chapter 17: 指针高级用法 动态存储分配 动态存储分配最常用到的地方时字符串、数组 和结构. 动态分配的结构可以连接起来形成列表、树和 其他数据结构. 动态存储分配使用的是内存分配函数. Copyright © 2008 W. W. Norton & Company. All rights reserved. 3

Chapter 17: 指针高级用法 内存分配函数 头文件声明三个内容分配函数 : malloc — 分配一块内容但并不初始化它. calloc — 分配一块内容且对内存卡清零. realloc — 重新分配前面分配内容块的大小. 这些函数返回值类型是 void * ( 一个 “ 通用 generic” 指针 ). Copyright © 2008 W. W. Norton & Company. All rights reserved. 4

Chapter 17: 指针高级用法 Null Pointers 如果一个内存分配函数没能分配一个需要大小 的内存块, 则该函数返回一个空指针 null pointer. 空指针时一类特殊指针,它可同所有有效的指 针相区分. 当我们在一个指针变量中存储了一个函数返回 值,我们必须测试产看它是否是空指针. Copyright © 2008 W. W. Norton & Company. All rights reserved. 5

Chapter 17: 指针高级用法 空指针 Null Pointers 测试 malloc 函数返回值的例子 : p = malloc(10000); if (p == NULL) { /* 分配失败,采取其他行动 */ } NULL 是一个宏 ( 定义在各种库的头文件中 ) 它表 示一个空指针. 某些程序员结合 NULL 测试来调用 malloc : if ((p = malloc(10000)) == NULL) { /* allocation failed; take appropriate action */ } Copyright © 2008 W. W. Norton & Company. All rights reserved. 6

Chapter 17: 指针高级用法 空指针 指针可以和数字一样,可以测试真或假. 所有非空指针测试为真 ; 而只有空指针测试为假. 我们不写 if (p == NULL) … 相反,我们写 if (!p) … 类似地,不写 if (p != NULL) … 而写 if (p) … Copyright © 2008 W. W. Norton & Company. All rights reserved. 7

Chapter 17: 指针高级用法 动态分配字符串 动态存储分配与字符串一起工作十分有用. 我们已经知道,字符串是存储在字符数组中的 ,而且我们很难估计这些数组需要多长合适. 通过动态分配字符串, 我们能够推迟决定其大 小,直到程序运行的时侯再确定. Copyright © 2008 W. W. Norton & Company. All rights reserved. 8

Chapter 17: 指针高级用法 使用 malloc 为字符串分配内存 malloc 函数的原型 : void *malloc(size_t size); malloc 分配 size 字节的块并返回一个指向 它的指针. size_t 是一个无符号整数类型,定义在 C 库 中. Copyright © 2008 W. W. Norton & Company. All rights reserved. 9

Chapter 17: 指针高级用法 使用 malloc 为字符串分配内存 调用 malloc 函数分配 n 个字符的字符串内存 : p = malloc(n + 1); p 是一个 char * 变量. 每个字符需要一个字节内存 ; 增加 1 到 n 为空 字符留一个空间. 某些程序员喜欢对 malloc 函数的返回值强制 (cast) 类型转换, 结果强制式不需要的 : p = (char *) malloc(n + 1); 当使用 malloc 函数为字符串分配内存空间时, 不要忘 了包含空字符的空间。 Copyright © 2008 W. W. Norton & Company. All rights reserved. 10

Chapter 17: 指针高级用法 使用 malloc 为字符串分配内存 由于使用 malloc 分配的内存空间不需要清零 或者任何方式进行初始化, 因此 p 将指向带有 n + 1 个字符的未初始化的数组 : Copyright © 2008 W. W. Norton & Company. All rights reserved. 11

Chapter 17: 指针高级用法 使用 malloc 为字符串分配内存 调用 strcpy 是初始化这种数组的一种方法 : strcpy(p, "abc"); 数组的前 4 个字符分别为 a, b, c, and \0 : Copyright © 2008 W. W. Norton & Company. All rights reserved. 12

Chapter 17: 指针高级用法 在字符串函数中使用动态存储分配 动态存储分配使得写一个返回指向 “ 新 ” 的字符 串的指针变为可能. 所谓新字符串指的是调用此 函数之前该串不存在。 考虑编写一个函数,将两个字符串串联起来而 不改变其中任何一个字符串. 函数将测试将要串联的两个字符串的长度, 然后 调用 malloc 函数分配合适大小的内存空间. 接下来,函数会把第一个字符串复制到新的内 存空间中,且调用 strcat 函数拼接第二个字符 串。 Copyright © 2008 W. W. Norton & Company. All rights reserved. 13

Chapter 17: 指针高级用法 在字符串函数中使用动态存储分配 char *concat(const char *s1, const char *s2) { char *result; result = malloc(strlen(s1) + strlen(s2) + 1); if (result == NULL) { printf("Error: malloc failed in concat\n"); exit(EXIT_FAILURE); } strcpy(result, s1); strcat(result, s2); return result; } Copyright © 2008 W. W. Norton & Company. All rights reserved. 14

Chapter 17: 指针高级用法 在字符串函数中使用动态存储分配 调用 concat 函数的形式如下 : p = concat("abc", "def"); 调用之后, p 将指向字符串 “abcdef”, 该串是 存在在动态分配的数组中. 数组共有 7 个字符, 包含 1 个空字符。 当不再使用返回的字符串时,需要调用 free 函 数,来释放它占用的内存空间。 Copyright © 2008 W. W. Norton & Company. All rights reserved. 15

Chapter 17: 指针高级用法 程序 : 打印一个月的提示列表 (Revisited) remind2.c 程序是基于第 13 章中的 remind.c, 显示一个月的日常提醒列表. 原始 remind.c 程序将提醒字符串储存在一个 二维字符数组中. 在信程序中, 数组将是一维的 ; 其元素是指向动 态分配的字符串的指针. Copyright © 2008 W. W. Norton & Company. All rights reserved. 16

Chapter 17: 指针高级用法 程序 : 打印一个月的提示列表 (Revisited) 动态分配字符串的优点 : – 通过分配精确的字符数量需要的空间,从而可以更 有效地利用空间. – 避免调用 strcpy 来移动已近存在的(存放提醒) 字符串,只需移动指向字符串的指针. 从二维数组转化成指针数组只需改变 8 行程序 ( 显示用粗体字 bold). Copyright © 2008 W. W. Norton & Company. All rights reserved. 17

Chapter 17: 指针高级用法 remind2.c /* Prints a one-month reminder list (dynamic string version) */ #include #define MAX_REMIND 50 /* maximum number of reminders */ #define MSG_LEN 60 /* max length of reminder message */ int read_line(char str[], int n); int main(void) { char *reminders[MAX_REMIND]; char day_str[3], msg_str[MSG_LEN+1]; int day, i, j, num_remind = 0; Copyright © 2008 W. W. Norton & Company. All rights reserved. 18

Chapter 17: 指针高级用法 for (;;) { if (num_remind == MAX_REMIND) { printf("-- No space left --\n"); break; } printf("Enter day and reminder: "); scanf("%2d", &day); if (day == 0) break; sprintf(day_str, "%2d", day); read_line(msg_str, MSG_LEN); for (i = 0; i < num_remind; i++) if (strcmp(day_str, reminders[i]) < 0) break; for (j = num_remind; j > i; j--) reminders[j] = reminders[j-1]; Copyright © 2008 W. W. Norton & Company. All rights reserved. 19

Chapter 17: 指针高级用法 reminders[i] = malloc(2 + strlen(msg_str) + 1); if (reminders[i] == NULL) { printf("-- No space left --\n"); break; } strcpy(reminders[i], day_str); strcat(reminders[i], msg_str); num_remind++; } printf("\nDay Reminder\n"); for (i = 0; i < num_remind; i++) printf(" %s\n", reminders[i]); return 0; } Copyright © 2008 W. W. Norton & Company. All rights reserved. 20

Chapter 17: 指针高级用法 int read_line(char str[], int n) { int ch, i = 0; while ((ch = getchar()) != '\n') if (i < n) str[i++] = ch; str[i] = '\0'; return i; } Copyright © 2008 W. W. Norton & Company. All rights reserved. 21

Chapter 17: 指针高级用法 动态分配数组 动态分配组数与动态分配字符串一样的优点. 由于指针与数组之间的密切关系,使得动态分 配数组更容易像普通数组一样使用. 尽管 malloc 可以为数组分配空间, 有时 calloc 函数更经常使用, 因为它可以初始化分 配的内存. 而 realloc 函数允许我们按需增长或减少数 组的大小. Copyright © 2008 W. W. Norton & Company. All rights reserved. 22

Chapter 17: 指针高级用法 使用 malloc 为数组分配存储空间 假设程序正在需要 n 个整数的数组,这里的 n 是在程序执行期间计算出来的. 首先声明指向整数的指针变量 : int *a; 一旦 知道 n 的具体值, 程序就调用 malloc 函 数来为数组分配空间 : a = malloc(n * sizeof(int)); 计算数组所需的空间数量时,总是使用 sizeof 函数运算大小. 如果直接使用 a= malloc(n * 2); 只分配了 2*n 个 大小的空间,而不是 n* 具体的整数占用大小。 Copyright © 2008 W. W. Norton & Company. All rights reserved. 23

Chapter 17: 指针高级用法 使用 malloc 为数组分配存储空间 一旦 a 指向了冬天分配的内存块,就可忽略 a 是一个指针,而把他当做数组来使用。这就是 C 语言中指针和数组密切关系的好处。 比如,可以使用下列循环来对 a 指向的数组进 行初始化 : for (i = 0; i < n; i++) a[i] = 0; 当然,用指针算术运算替代数组下标运算来访 问数组元素也是可以的。 Copyright © 2008 W. W. Norton & Company. All rights reserved. 24

Chapter 17: 指针高级用法 The calloc Function The calloc function is an alternative to malloc. Prototype for calloc : void *calloc(size_t nmemb, size_t size); Properties of calloc : –Allocates space for an array with nmemb elements, each of which is size bytes long. –Returns a null pointer if the requested space isn’t available. –Initializes allocated memory by setting all bits to 0. Copyright © 2008 W. W. Norton & Company. All rights reserved. 25

Chapter 17: 指针高级用法 The calloc Function A call of calloc that allocates space for an array of n integers: a = calloc(n, sizeof(int)); By calling calloc with 1 as its first argument, we can allocate space for a data item of any type: struct point { int x, y; } *p; p = calloc(1, sizeof(struct point)); Copyright © 2008 W. W. Norton & Company. All rights reserved. 26

Chapter 17: 指针高级用法 The realloc Function The realloc function can resize a dynamically allocated array. Prototype for realloc : void *realloc(void *ptr, size_t size); ptr must point to a memory block obtained by a previous call of malloc, calloc, or realloc. size represents the new size of the block, which may be larger or smaller than the original size. Copyright © 2008 W. W. Norton & Company. All rights reserved. 27

Chapter 17: 指针高级用法 The realloc Function Properties of realloc : –When it expands a memory block, realloc doesn’t initialize the bytes that are added to the block. –If realloc can’t enlarge the memory block as requested, it returns a null pointer; the data in the old memory block is unchanged. –If realloc is called with a null pointer as its first argument, it behaves like malloc. –If realloc is called with 0 as its second argument, it frees the memory block. Copyright © 2008 W. W. Norton & Company. All rights reserved. 28

Chapter 17: 指针高级用法 The realloc Function We expect realloc to be reasonably efficient: –When asked to reduce the size of a memory block, realloc should shrink the block “in place.” –realloc should always attempt to expand a memory block without moving it. If it can’t enlarge a block, realloc will allocate a new block elsewhere, then copy the contents of the old block into the new one. Once realloc has returned, be sure to update all pointers to the memory block in case it has been moved. Copyright © 2008 W. W. Norton & Company. All rights reserved. 29

Chapter 17: 指针高级用法 Deallocating Storage malloc and the other memory allocation functions obtain memory blocks from a storage pool known as the heap. Calling these functions too often—or asking them for large blocks of memory—can exhaust the heap, causing the functions to return a null pointer. To make matters worse, a program may allocate blocks of memory and then lose track of them, thereby wasting space. Copyright © 2008 W. W. Norton & Company. All rights reserved. 30

Chapter 17: 指针高级用法 Deallocating Storage Example: p = malloc(…); q = malloc(…); p = q; A snapshot after the first two statements have been executed: Copyright © 2008 W. W. Norton & Company. All rights reserved. 31

Chapter 17: 指针高级用法 Deallocating Storage After q is assigned to p, both variables now point to the second memory block: There are no pointers to the first block, so we’ll never be able to use it again. Copyright © 2008 W. W. Norton & Company. All rights reserved. 32

Chapter 17: 指针高级用法 Deallocating Storage A block of memory that’s no longer accessible to a program is said to be garbage. A program that leaves garbage behind has a memory leak. Some languages provide a garbage collector that automatically locates and recycles garbage, but C doesn’t. Instead, each C program is responsible for recycling its own garbage by calling the free function to release unneeded memory. Copyright © 2008 W. W. Norton & Company. All rights reserved. 33

Chapter 17: 指针高级用法 Free 函数 free 函数原型 : void free(void *ptr); free 将传递一个指向不需要的内存块的指针 : p = malloc(…); q = malloc(…); free(p); p = q; 调用 free 函数释放 p 指针指向的内存块. 这些 释放的内存块可被后续的 malloc 函数或其他内 存分配函数重新使用。 Copyright © 2008 W. W. Norton & Company. All rights reserved. 34

Chapter 17: 指针高级用法 “ 悬空指针 Dangling Pointer” 问题 使用 free 函数导致新问题:悬空指针. free(p) 释放了 p 指向的内存块, 但指针 p 没有改 变. 比如, 如果我们忘记了 p 不再指向任何有效的内存块了 ,胡乱调用指针 p 可能引起灾难 : char *p = malloc(4); … free(p); … strcpy(p, "abc"); /*** WRONG ***/ Copyright © 2008 W. W. Norton & Company. All rights reserved. 35

Chapter 17: 指针高级用法 The “Dangling Pointer” Problem 悬空指针很难发现,因为有可能有几个指针同 时指向同一内存块. 当内存块释放后, 所有指针将被悬空. Copyright © 2008 W. W. Norton & Company. All rights reserved. 36

Chapter 17: 指针高级用法 链表 Linked Lists 动态内容分配特别有用的地方就在一建立链表 、树、图和其他数据结构. 一个链表 linked list 由一连串的结构链组成,结 构叫做节点,其中每个节点都包含指向下一个 节点的指针 : 最后一个节点包含空指针. Copyright © 2008 W. W. Norton & Company. All rights reserved. 37

Chapter 17: 指针高级用法 链表 链表比数组更灵活 : 我们更容易插入和删除链 表中的节点, 使得链表按需扩大或缩小. 一方面, 我们损失了自由访问数组的能力 : – 任何数组元素可可以在同时段内进行访问. – 但访问链表中的节点不同,如果位于在链表开始的 节点,访问就较快,但位于末尾的节点,访问就较 慢。 Copyright © 2008 W. W. Norton & Company. All rights reserved. 38

Chapter 17: 指针高级用法 声明节点类型 为了建立链表, 我们需要一个结构来表示节点. 简单例子:一个节点结构包含一个数和一个指向 下一个节点的指针 : struct node { int value; /* data stored in the node */ struct node *next; /* pointer to the next node */ }; node 必须为一个标签, 而不能是一个 typedef 名, 或者没有其他方法可以声明 next 指针. Copyright © 2008 W. W. Norton & Company. All rights reserved. 39

Chapter 17: 指针高级用法 声明节点类型 下一步,我们需要一个变量,该变量总是指向 链表中的第一个节点 : struct node *first = NULL; 设置 first 指向 NULL 意味着链表是个初始的 空表。 Copyright © 2008 W. W. Norton & Company. All rights reserved. 40

Chapter 17: 指针高级用法 创建节点 在构造链表时,需要逐个创建, 每次增加一个 节点. 创建一个节点的步骤 : 1. 首先分配节点的内存. 2. 然后在节点中存放数据. 3. 再插入节点进链表. 现在我们关注前两步. Copyright © 2008 W. W. Norton & Company. All rights reserved. 41

Chapter 17: 指针高级用法 创建节点 首先建立一个指向节点的指针 : struct node *new_node; 然后使用 malloc 函数为下一个节点分配内存, 将返回值存放在 new_node : new_node = malloc(sizeof(struct node)); new_node 现在指向了一个内存块,该块的大 小正好存放一个 node 结构的大小 : Copyright © 2008 W. W. Norton & Company. All rights reserved. 42

Chapter 17: 指针高级用法 创建节点 下一步, 我们存放数据进新节点中 : (*new_node).value = 10; 图示结果如下 : 扩住 *new_node 的圆括号是必须的,因为. 运算符的优先级比 * 运算符高. Copyright © 2008 W. W. Norton & Company. All rights reserved. 43

Chapter 17: 指针高级用法 -> 运算符 使用指针访问结构成员是如此频繁,所以 C 语 言为此专门设计了一个运算符. 这个预算符, 称作右箭头选择 right arrow selection, 一个减号加一个尖括号 >. 使用 -> 运算符, 我们可以写成 new_node->value = 10; 而不用 (*new_node).value = 10; Copyright © 2008 W. W. Norton & Company. All rights reserved. 44

Chapter 17: 指针高级用法 -> 运算符 -> 预算符产生左值, 所以我们可以按照普通变 量那样使用它. 比如,在 scanf 函数中 : scanf("%d", &new_node->value); & 运算符仍然需要, 即使 new_node 是一个指 针. Copyright © 2008 W. W. Norton & Company. All rights reserved. 45

Chapter 17: 指针高级用法 在链表开始出插入一个节点 链表的一个优点是可以在任何节点插入新节点. 但是, 链表的开始处是插入阶段的最容易的地 方. 假设 new_node 指向了节点需要插入,而 first 指针指向了第一个节点. Copyright © 2008 W. W. Norton & Company. All rights reserved. 46

Chapter 17: 指针高级用法 在链表开始出插入一个节点 在链表中插入一个节点需要两条语句来完成。 第一步是修改新节点 next ,使其指向先前在 链表开始处的节点 : new_node->next = first; 第二步:使 first 指向新节点 : first = new_node; 如果链表是空链表,这些语句是有效的. Copyright © 2008 W. W. Norton & Company. All rights reserved. 47

Chapter 17: 指针高级用法 在链表开始出插入一个节点 让我跟踪一下插入两个节点到一个空链表的过 程. 首先插入包含数字 10 的节点, 然后插入包含数 字 20 的节点. Copyright © 2008 W. W. Norton & Company. All rights reserved. 48

Chapter 17: 指针高级用法 first = NULL; new_node = malloc(sizeof(struct node)); new_node->value = 10; 在链表开始出插入一个节点 Copyright © 2008 W. W. Norton & Company. All rights reserved. 49

Chapter 17: 指针高级用法 new_node->next = first; first = new_node; new_node = malloc(sizeof(struct node)); 在链表开始出插入一个节点 Copyright © 2008 W. W. Norton & Company. All rights reserved. 50

Chapter 17: 指针高级用法 在链表开始出插入一个节点 new_node->value = 20; new_node->next = first; first = new_node; Copyright © 2008 W. W. Norton & Company. All rights reserved. 51

Chapter 17: 指针高级用法 在链表开始出插入一个节点 插入函数在链表的开始处插入一个包含数 n 的 节点 : struct node *add_to_list(struct node *list, int n) { struct node *new_node; new_node = malloc(sizeof(struct node)); if (new_node == NULL) { printf("Error: malloc failed in add_to_list\n"); exit(EXIT_FAILURE); } new_node->value = n; new_node->next = list; return new_node; } Copyright © 2008 W. W. Norton & Company. All rights reserved. 52

Chapter 17: 指针高级用法 在链表开始出插入一个节点 注意 add_to_list 返回一个指针,该指针指 向新创立的节点,而该节点现在是位于链表的 开始处,而没有改变 list 指针。 当调用 add_to_list, 需要将其返回值存储到 first : first = add_to_list(first, 10); first = add_to_list(first, 20); 调用 add_to_list 直接更新 first 指针, 而 不是为 first 返回一个新值, 这是一个技巧. Copyright © 2008 W. W. Norton & Company. All rights reserved. 53

Chapter 17: 指针高级用法 在链表开始出插入一个节点 下面的函数使用 add_to_list 来创建包含用户输入 数字的链表 : struct node *read_numbers(void) { struct node *first = NULL; int n; printf("Enter a series of integers (0 to terminate): "); for (;;) { scanf("%d", &n); if (n == 0) return first; first = add_to_list(first, n); } 用户输入的数将是倒置在链表中的. Copyright © 2008 W. W. Norton & Company. All rights reserved. 54

Chapter 17: 指针高级用法 搜素链表 Although a while loop can be used to search a list, the for statement is often superior. A loop that visits the nodes in a linked list, using a pointer variable p to keep track of the “current” node: for (p = first; p != NULL; p = p->next) … A loop of this form can be used in a function that searches a list for an integer n. Copyright © 2008 W. W. Norton & Company. All rights reserved. 55

Chapter 17: 指针高级用法 Searching a Linked List If it finds n, the function will return a pointer to the node containing n ; otherwise, it will return a null pointer. An initial version of the function: struct node *search_list(struct node *list, int n) { struct node *p; for (p = list; p != NULL; p = p->next) if (p->value == n) return p; return NULL; } Copyright © 2008 W. W. Norton & Company. All rights reserved. 56

Chapter 17: 指针高级用法 Searching a Linked List There are many other ways to write search_list. One alternative is to eliminate the p variable, instead using list itself to keep track of the current node: struct node *search_list(struct node *list, int n) { for (; list != NULL; list = list->next) if (list->value == n) return list; return NULL; } Since list is a copy of the original list pointer, there’s no harm in changing it within the function. Copyright © 2008 W. W. Norton & Company. All rights reserved. 57

Chapter 17: 指针高级用法 Searching a Linked List Another alternative: struct node *search_list(struct node *list, int n) { for (; list != NULL && list->value != n; list = list->next) ; return list; } Since list is NULL if we reach the end of the list, returning list is correct even if we don’t find n. Copyright © 2008 W. W. Norton & Company. All rights reserved. 58

Chapter 17: 指针高级用法 Searching a Linked List This version of search_list might be a bit clearer if we used a while statement: struct node *search_list(struct node *list, int n) { while (list != NULL && list->value != n) list = list->next; return list; } Copyright © 2008 W. W. Norton & Company. All rights reserved. 59

Chapter 17: 指针高级用法 Deleting a Node from a Linked List A big advantage of storing data in a linked list is that we can easily delete nodes. Deleting a node involves three steps: 1.Locate the node to be deleted. 2.Alter the previous node so that it “bypasses” the deleted node. 3.Call free to reclaim the space occupied by the deleted node. Step 1 is harder than it looks, because step 2 requires changing the previous node. There are various solutions to this problem. Copyright © 2008 W. W. Norton & Company. All rights reserved. 60

Chapter 17: 指针高级用法 Deleting a Node from a Linked List The “trailing pointer” technique involves keeping a pointer to the previous node ( prev ) as well as a pointer to the current node ( cur ). Assume that list points to the list to be searched and n is the integer to be deleted. A loop that implements step 1: for (cur = list, prev = NULL; cur != NULL && cur->value != n; prev = cur, cur = cur->next) ; When the loop terminates, cur points to the node to be deleted and prev points to the previous node. Copyright © 2008 W. W. Norton & Company. All rights reserved. 61

Chapter 17: 指针高级用法 Deleting a Node from a Linked List Assume that list has the following appearance and n is 20: After cur = list, prev = NULL has been executed: Copyright © 2008 W. W. Norton & Company. All rights reserved. 62

Chapter 17: 指针高级用法 Deleting a Node from a Linked List The test cur != NULL && cur->value != n is true, since cur is pointing to a node and the node doesn’t contain 20. After prev = cur, cur = cur->next has been executed: Copyright © 2008 W. W. Norton & Company. All rights reserved. 63

Chapter 17: 指针高级用法 Deleting a Node from a Linked List The test cur != NULL && cur->value != n is again true, so prev = cur, cur = cur->next is executed once more: Since cur now points to the node containing 20, the condition cur->value != n is false and the loop terminates. Copyright © 2008 W. W. Norton & Company. All rights reserved. 64

Chapter 17: 指针高级用法 Deleting a Node from a Linked List Next, we’ll perform the bypass required by step 2. The statement prev->next = cur->next; makes the pointer in the previous node point to the node after the current node: Copyright © 2008 W. W. Norton & Company. All rights reserved. 65

Chapter 17: 指针高级用法 Deleting a Node from a Linked List Step 3 is to release the memory occupied by the current node: free(cur); Copyright © 2008 W. W. Norton & Company. All rights reserved. 66

Chapter 17: 指针高级用法 Deleting a Node from a Linked List The delete_from_list function uses the strategy just outlined. When given a list and an integer n, the function deletes the first node containing n. If no node contains n, delete_from_list does nothing. In either case, the function returns a pointer to the list. Deleting the first node in the list is a special case that requires a different bypass step. Copyright © 2008 W. W. Norton & Company. All rights reserved. 67

Chapter 17: 指针高级用法 Deleting a Node from a Linked List struct node *delete_from_list(struct node *list, int n) { struct node *cur, *prev; for (cur = list, prev = NULL; cur != NULL && cur->value != n; prev = cur, cur = cur->next) ; if (cur == NULL) return list; /* n was not found */ if (prev == NULL) list = list->next; /* n is in the first node */ else prev->next = cur->next; /* n is in some other node */ free(cur); return list; } Copyright © 2008 W. W. Norton & Company. All rights reserved. 68

Chapter 17: 指针高级用法 Ordered Lists When the nodes of a list are kept in order—sorted by the data stored inside the nodes—we say that the list is ordered. Inserting a node into an ordered list is more difficult, because the node won’t always be put at the beginning of the list. However, searching is faster: we can stop looking after reaching the point at which the desired node would have been located. Copyright © 2008 W. W. Norton & Company. All rights reserved. 69

Chapter 17: 指针高级用法 Program: Maintaining a Parts Database (Revisited) The inventory2.c program is a modification of the parts database program of Chapter 16, with the database stored in a linked list this time. Advantages of using a linked list: –No need to put a limit on the size of the database. –Database can easily be kept sorted by part number. In the original program, the database wasn’t sorted. Copyright © 2008 W. W. Norton & Company. All rights reserved. 70

Chapter 17: 指针高级用法 Program: Maintaining a Parts Database (Revisited) The part structure will contain an additional member (a pointer to the next node): struct part { int number; char name[NAME_LEN+1]; int on_hand; struct part *next; }; inventory will point to the first node in the list: struct part *inventory = NULL; Copyright © 2008 W. W. Norton & Company. All rights reserved. 71

Chapter 17: 指针高级用法 Program: Maintaining a Parts Database (Revisited) Most of the functions in the new program will closely resemble their counterparts in the original program. find_part and insert will be more complex, however, since we’ll keep the nodes in the inventory list sorted by part number. Copyright © 2008 W. W. Norton & Company. All rights reserved. 72

Chapter 17: 指针高级用法 Program: Maintaining a Parts Database (Revisited) In the original program, find_part returns an index into the inventory array. In the new program, find_part will return a pointer to the node that contains the desired part number. If it doesn’t find the part number, find_part will return a null pointer. Copyright © 2008 W. W. Norton & Company. All rights reserved. 73

Chapter 17: 指针高级用法 Program: Maintaining a Parts Database (Revisited) Since the list of parts is sorted, find_part can stop when it finds a node containing a part number that’s greater than or equal to the desired part number. find_part ’s search loop: for (p = inventory; p != NULL && number > p->number; p = p->next) ; When the loop terminates, we’ll need to test whether the part was found: if (p != NULL && number == p->number) return p; Copyright © 2008 W. W. Norton & Company. All rights reserved. 74

Chapter 17: 指针高级用法 Program: Maintaining a Parts Database (Revisited) The original version of insert stores a new part in the next available array element. The new version must determine where the new part belongs in the list and insert it there. It will also check whether the part number is already present in the list. A loop that accomplishes both tasks: for (cur = inventory, prev = NULL; cur != NULL && new_node->number > cur->number; prev = cur, cur = cur->next) ; Copyright © 2008 W. W. Norton & Company. All rights reserved. 75

Chapter 17: 指针高级用法 Program: Maintaining a Parts Database (Revisited) Once the loop terminates, insert will check whether cur isn’t NULL and whether new_node->number equals cur->number. –If both are true, the part number is already in the list. –Otherwise, insert will insert a new node between the nodes pointed to by prev and cur. This strategy works even if the new part number is larger than any in the list. Like the original program, this version requires the read_line function of Chapter 16. Copyright © 2008 W. W. Norton & Company. All rights reserved. 76

Chapter 17: 指针高级用法 inventory2.c /* Maintains a parts database (linked list version) */ #include #include "readline.h" #define NAME_LEN 25 struct part { int number; char name[NAME_LEN+1]; int on_hand; struct part *next; }; struct part *inventory = NULL; /* points to first part */ struct part *find_part(int number); void insert(void); void search(void); void update(void); void print(void); Copyright © 2008 W. W. Norton & Company. All rights reserved. 77

Chapter 17: 指针高级用法 /********************************************************** * main: Prompts the user to enter an operation code, * * then calls a function to perform the requested * * action. Repeats until the user enters the * * command 'q'. Prints an error message if the user * * enters an illegal code. * **********************************************************/ int main(void) { char code; for (;;) { printf("Enter operation code: "); scanf(" %c", &code); while (getchar() != '\n') /* skips to end of line */ ; Copyright © 2008 W. W. Norton & Company. All rights reserved. 78

Chapter 17: 指针高级用法 switch (code) { case 'i': insert(); break; case 's': search(); break; case 'u': update(); break; case 'p': print(); break; case 'q': return 0; default: printf("Illegal code\n"); } printf("\n"); } Copyright © 2008 W. W. Norton & Company. All rights reserved. 79

Chapter 17: 指针高级用法 /********************************************************** * find_part: Looks up a part number in the inventory * * list. Returns a pointer to the node * * containing the part number; if the part * * number is not found, returns NULL. * **********************************************************/ struct part *find_part(int number) { struct part *p; for (p = inventory; p != NULL && number > p->number; p = p->next) ; if (p != NULL && number == p->number) return p; return NULL; } Copyright © 2008 W. W. Norton & Company. All rights reserved. 80

Chapter 17: 指针高级用法 /********************************************************** * insert: Prompts the user for information about a new * * part and then inserts the part into the * * inventory list; the list remains sorted by * * part number. Prints an error message and * * returns prematurely if the part already exists * * or space could not be allocated for the part. * **********************************************************/ void insert(void) { struct part *cur, *prev, *new_node; new_node = malloc(sizeof(struct part)); if (new_node == NULL) { printf("Database is full; can't add more parts.\n"); return; } printf("Enter part number: "); scanf("%d", &new_node->number); Copyright © 2008 W. W. Norton & Company. All rights reserved. 81

Chapter 17: 指针高级用法 for (cur = inventory, prev = NULL; cur != NULL && new_node->number > cur->number; prev = cur, cur = cur->next) ; if (cur != NULL && new_node->number == cur->number) { printf("Part already exists.\n"); free(new_node); return; } printf("Enter part name: "); read_line(new_node->name, NAME_LEN); printf("Enter quantity on hand: "); scanf("%d", &new_node->on_hand); new_node->next = cur; if (prev == NULL) inventory = new_node; else prev->next = new_node; } Copyright © 2008 W. W. Norton & Company. All rights reserved. 82

Chapter 17: 指针高级用法 /********************************************************** * search: Prompts the user to enter a part number, then * * looks up the part in the database. If the part * * exists, prints the name and quantity on hand; * * if not, prints an error message. * **********************************************************/ void search(void) { int number; struct part *p; printf("Enter part number: "); scanf("%d", &number); p = find_part(number); if (p != NULL) { printf("Part name: %s\n", p->name); printf("Quantity on hand: %d\n", p->on_hand); } else printf("Part not found.\n"); } Copyright © 2008 W. W. Norton & Company. All rights reserved. 83

Chapter 17: 指针高级用法 /********************************************************** * update: Prompts the user to enter a part number. * * Prints an error message if the part doesn't * * exist; otherwise, prompts the user to enter * * change in quantity on hand and updates the * * database. * **********************************************************/ void update(void) { int number, change; struct part *p; printf("Enter part number: "); scanf("%d", &number); p = find_part(number); if (p != NULL) { printf("Enter change in quantity on hand: "); scanf("%d", &change); p->on_hand += change; } else printf("Part not found.\n"); } Copyright © 2008 W. W. Norton & Company. All rights reserved. 84

Chapter 17: 指针高级用法 /********************************************************** * print: Prints a listing of all parts in the database, * * showing the part number, part name, and * * quantity on hand. Part numbers will appear in * * ascending order. * **********************************************************/ void print(void) { struct part *p; printf("Part Number Part Name " "Quantity on Hand\n"); for (p = inventory; p != NULL; p = p->next) printf("%7d %-25s%11d\n", p->number, p->name, p->on_hand); } Copyright © 2008 W. W. Norton & Company. All rights reserved. 85

Chapter 17: 指针高级用法 Pointers to Pointers Chapter 13 introduced the idea of a pointer to a pointer. The concept of “pointers to pointers” also pops up frequently in the context of linked data structures. In particular, when an argument to a function is a pointer variable, we may want the function to be able to modify the variable. Doing so requires the use of a pointer to a pointer. Copyright © 2008 W. W. Norton & Company. All rights reserved. 86

Chapter 17: 指针高级用法 Pointers to Pointers The add_to_list function is passed a pointer to the first node in a list; it returns a pointer to the first node in the updated list: struct node *add_to_list(struct node *list, int n) { struct node *new_node; new_node = malloc(sizeof(struct node)); if (new_node == NULL) { printf("Error: malloc failed in add_to_list\n"); exit(EXIT_FAILURE); } new_node->value = n; new_node->next = list; return new_node; } Copyright © 2008 W. W. Norton & Company. All rights reserved. 87

Chapter 17: 指针高级用法 Pointers to Pointers Modifying add_to_list so that it assigns new_node to list instead of returning new_node doesn’t work. Example: add_to_list(first, 10); At the point of the call, first is copied into list. If the function changes the value of list, making it point to the new node, first is not affected. Copyright © 2008 W. W. Norton & Company. All rights reserved. 88

Chapter 17: 指针高级用法 Pointers to Pointers Getting add_to_list to modify first requires passing add_to_list a pointer to first : void add_to_list(struct node **list, int n) { struct node *new_node; new_node = malloc(sizeof(struct node)); if (new_node == NULL) { printf("Error: malloc failed in add_to_list\n"); exit(EXIT_FAILURE); } new_node->value = n; new_node->next = *list; *list = new_node; } Copyright © 2008 W. W. Norton & Company. All rights reserved. 89

Chapter 17: 指针高级用法 Pointers to Pointers When the new version of add_to_list is called, the first argument will be the address of first : add_to_list(&first, 10); Since list is assigned the address of first, we can use *list as an alias for first. In particular, assigning new_node to *list will modify first. Copyright © 2008 W. W. Norton & Company. All rights reserved. 90

Chapter 17: 指针高级用法 Pointers to Functions C doesn’t require that pointers point only to data; it’s also possible to have pointers to functions. Functions occupy memory locations, so every function has an address. We can use function pointers in much the same way we use pointers to data. Passing a function pointer as an argument is fairly common. Copyright © 2008 W. W. Norton & Company. All rights reserved. 91

Chapter 17: 指针高级用法 Function Pointers as Arguments A function named integrate that integrates a mathematical function f can be made as general as possible by passing f as an argument. Prototype for integrate : double integrate(double (*f)(double), double a, double b); The parentheses around *f indicate that f is a pointer to a function. An alternative prototype: double integrate(double f(double), double a, double b); Copyright © 2008 W. W. Norton & Company. All rights reserved. 92

Chapter 17: 指针高级用法 Function Pointers as Arguments A call of integrate that integrates the sin (sine) function from 0 to  /2: result = integrate(sin, 0.0, PI / 2); When a function name isn’t followed by parentheses, the C compiler produces a pointer to the function. Within the body of integrate, we can call the function that f points to: y = (*f)(x); Writing f(x) instead of (*f)(x) is allowed. Copyright © 2008 W. W. Norton & Company. All rights reserved. 93

Chapter 17: 指针高级用法 The qsort Function Some of the most useful functions in the C library require a function pointer as an argument. One of these is qsort, which belongs to the header. qsort is a general-purpose sorting function that’s capable of sorting any array. Copyright © 2008 W. W. Norton & Company. All rights reserved. 94

Chapter 17: 指针高级用法 The qsort Function qsort must be told how to determine which of two array elements is “smaller.” This is done by passing qsort a pointer to a comparison function. When given two pointers p and q to array elements, the comparison function must return an integer that is: –Negative if *p is “less than” *q –Zero if *p is “equal to” *q –Positive if *p is “greater than” *q Copyright © 2008 W. W. Norton & Company. All rights reserved. 95

Chapter 17: 指针高级用法 The qsort Function Prototype for qsort : void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *)); base must point to the first element in the array (or the first element in the portion to be sorted). nmemb is the number of elements to be sorted. size is the size of each array element, measured in bytes. compar is a pointer to the comparison function. Copyright © 2008 W. W. Norton & Company. All rights reserved. 96

Chapter 17: 指针高级用法 The qsort Function When qsort is called, it sorts the array into ascending order, calling the comparison function whenever it needs to compare array elements. A call of qsort that sorts the inventory array of Chapter 16: qsort(inventory, num_parts, sizeof(struct part), compare_parts); compare_parts is a function that compares two part structures. Copyright © 2008 W. W. Norton & Company. All rights reserved. 97

Chapter 17: 指针高级用法 The qsort Function Writing the compare_parts function is tricky. qsort requires that its parameters have type void *, but we can’t access the members of a part structure through a void * pointer. To solve the problem, compare_parts will assign its parameters, p and q, to variables of type struct part *. Copyright © 2008 W. W. Norton & Company. All rights reserved. 98

Chapter 17: 指针高级用法 The qsort Function A version of compare_parts that can be used to sort the inventory array into ascending order by part number: int compare_parts(const void *p, const void *q) { const struct part *p1 = p; const struct part *q1 = q; if (p1->number number) return -1; else if (p1->number == q1->number) return 0; else return 1; } Copyright © 2008 W. W. Norton & Company. All rights reserved. 99

Chapter 17: 指针高级用法 The qsort Function Most C programmers would write the function more concisely: int compare_parts(const void *p, const void *q) { if (((struct part *) p)->number < ((struct part *) q)->number) return -1; else if (((struct part *) p)->number == ((struct part *) q)->number) return 0; else return 1; } Copyright © 2008 W. W. Norton & Company. All rights reserved. 100

Chapter 17: 指针高级用法 The qsort Function compare_parts can be made even shorter by removing the if statements: int compare_parts(const void *p, const void *q) { return ((struct part *) p)->number - ((struct part *) q)->number; } Copyright © 2008 W. W. Norton & Company. All rights reserved. 101

Chapter 17: 指针高级用法 The qsort Function A version of compare_parts that can be used to sort the inventory array by part name instead of part number: int compare_parts(const void *p, const void *q) { return strcmp(((struct part *) p)->name, ((struct part *) q)->name); } Copyright © 2008 W. W. Norton & Company. All rights reserved. 102

Chapter 17: 指针高级用法 Other Uses of Function Pointers Although function pointers are often used as arguments, that’s not all they’re good for. C treats pointers to functions just like pointers to data. They can be stored in variables or used as elements of an array or as members of a structure or union. It’s even possible for functions to return function pointers. Copyright © 2008 W. W. Norton & Company. All rights reserved. 103

Chapter 17: 指针高级用法 Other Uses of Function Pointers A variable that can store a pointer to a function with an int parameter and a return type of void : void (*pf)(int); If f is such a function, we can make pf point to f in the following way: pf = f; We can now call f by writing either (*pf)(i); or pf(i); Copyright © 2008 W. W. Norton & Company. All rights reserved. 104

Chapter 17: 指针高级用法 Other Uses of Function Pointers An array whose elements are function pointers: void (*file_cmd[])(void) = {new_cmd, open_cmd, close_cmd, close_all_cmd, save_cmd, save_as_cmd, save_all_cmd, print_cmd, exit_cmd }; Copyright © 2008 W. W. Norton & Company. All rights reserved. 105

Chapter 17: 指针高级用法 Other Uses of Function Pointers A call of the function stored in position n of the file_cmd array: (*file_cmd[n])(); /* or file_cmd[n](); */ We could get a similar effect with a switch statement, but using an array of function pointers provides more flexibility. Copyright © 2008 W. W. Norton & Company. All rights reserved. 106

Chapter 17: 指针高级用法 Program: Tabulating the Trigonometric Functions The tabulate.c program prints tables showing the values of the cos, sin, and tan functions. The program is built around a function named tabulate that, when passed a function pointer f, prints a table showing the values of f. tabulate uses the ceil function. When given an argument x of double type, ceil returns the smallest integer that’s greater than or equal to x. Copyright © 2008 W. W. Norton & Company. All rights reserved. 107

Chapter 17: 指针高级用法 Program: Tabulating the Trigonometric Functions A session with tabulate.c : Enter initial value: 0 Enter final value:.5 Enter increment:.1 x cos(x) Copyright © 2008 W. W. Norton & Company. All rights reserved. 108

Chapter 17: 指针高级用法 Program: Tabulating the Trigonometric Functions x sin(x) x tan(x) Copyright © 2008 W. W. Norton & Company. All rights reserved. 109

Chapter 17: 指针高级用法 tabulate.c /* Tabulates values of trigonometric functions */ #include void tabulate(double (*f)(double), double first, double last, double incr); int main(void) { double final, increment, initial; printf("Enter initial value: "); scanf("%lf", &initial); printf("Enter final value: "); scanf("%lf", &final); printf("Enter increment: "); scanf("%lf", &increment); Copyright © 2008 W. W. Norton & Company. All rights reserved. 110

Chapter 17: 指针高级用法 printf("\n x cos(x)" "\n \n"); tabulate(cos, initial, final, increment); printf("\n x sin(x)" "\n \n"); tabulate(sin, initial, final, increment); printf("\n x tan(x)" "\n \n"); tabulate(tan, initial, final, increment); return 0; } void tabulate(double (*f)(double), double first, double last, double incr) { double x; int i, num_intervals; num_intervals = ceil((last - first) / incr); for (i = 0; i <= num_intervals; i++) { x = first + i * incr; printf("%10.5f %10.5f\n", x, (*f)(x)); } Copyright © 2008 W. W. Norton & Company. All rights reserved. 111

Chapter 17: 指针高级用法 Restricted Pointers (C99) In C99, the keyword restrict may appear in the declaration of a pointer: int * restrict p; p is said to be a restricted pointer. The intent is that if p points to an object that is later modified, then that object is not accessed in any way other than through p. Having more than one way to access an object is often called aliasing. Copyright © 2008 W. W. Norton & Company. All rights reserved. 112

Chapter 17: 指针高级用法 Restricted Pointers (C99) Consider the following code: int * restrict p; int * restrict q; p = malloc(sizeof(int)); Normally it would be legal to copy p into q and then modify the integer through q : q = p; *q = 0; /* causes undefined behavior */ Because p is a restricted pointer, the effect of executing the statement *q = 0; is undefined. Copyright © 2008 W. W. Norton & Company. All rights reserved. 113

Chapter 17: 指针高级用法 Restricted Pointers (C99) To illustrate the use of restrict, consider the memcpy and memmove functions. The C99 prototype for memcpy, which copies bytes from one object (pointed to by s2 ) to another (pointed to by s1 ): void *memcpy(void * restrict s1, const void * restrict s2, size_t n); The use of restrict with both s1 and s2 indicates that the objects to which they point shouldn’t overlap. Copyright © 2008 W. W. Norton & Company. All rights reserved. 114

Chapter 17: 指针高级用法 Restricted Pointers (C99) In contrast, restrict doesn’t appear in the prototype for memmove : void *memmove(void *s1, const void *s2, size_t n); memmove is similar to memcpy, but is guaranteed to work even if the source and destination overlap. Example of using memmove to shift the elements of an array: int a[100]; … memmove(&a[0], &a[1], 99 * sizeof(int)); Copyright © 2008 W. W. Norton & Company. All rights reserved. 115

Chapter 17: 指针高级用法 Restricted Pointers (C99) Prior to C99, there was no way to document the difference between memcpy and memmove. The prototypes for the two functions were nearly identical: void *memcpy(void *s1, const void *s2, size_t n); void *memmove(void *s1, const void *s2, size_t n); The use of restrict in the C99 version of memcpy ’s prototype is a warning that the s1 and s2 objects should not overlap. Copyright © 2008 W. W. Norton & Company. All rights reserved. 116

Chapter 17: 指针高级用法 Restricted Pointers (C99) restrict provides information to the compiler that may enable it to produce more efficient code—a process known as optimization. The C99 standard guarantees that restrict has no effect on the behavior of a program that conforms to the standard. Most programmers won’t use restrict unless they’re fine-tuning a program to achieve the best possible performance. Copyright © 2008 W. W. Norton & Company. All rights reserved. 117

Chapter 17: 指针高级用法 Flexible Array Members (C99) Occasionally, we’ll need to define a structure that contains an array of an unknown size. For example, we might want a structure that stores the characters in a string together with the string’s length: struct vstring { int len; char chars[N]; }; Using a fixed-length array is undesirable: it limits the length of the string and wastes memory. Copyright © 2008 W. W. Norton & Company. All rights reserved. 118

Chapter 17: 指针高级用法 Flexible Array Members (C99) C programmers traditionally solve this problem by declaring the length of chars to be 1 and then dynamically allocating each string: struct vstring { int len; char chars[1]; }; … struct vstring *str = malloc(sizeof(struct vstring) + n - 1); str->len = n; This technique is known as the “struct hack.” Copyright © 2008 W. W. Norton & Company. All rights reserved. 119

Chapter 17: 指针高级用法 Flexible Array Members (C99) The struct hack is supported by many compilers. Some (including GCC) even allow the chars array to have zero length. The C89 standard doesn’t guarantee that the struct hack will work, but a C99 feature known as the flexible array member serves the same purpose. Copyright © 2008 W. W. Norton & Company. All rights reserved. 120

Chapter 17: 指针高级用法 Flexible Array Members (C99) When the last member of a structure is an array, its length may be omitted: struct vstring { int len; char chars[]; /* flexible array member - C99 only */ }; The length of the array isn’t determined until memory is allocated for a vstring structure: struct vstring *str = malloc(sizeof(struct vstring) + n); str->len = n; sizeof ignores the chars member when computing the size of the structure. Copyright © 2008 W. W. Norton & Company. All rights reserved. 121

Chapter 17: 指针高级用法 Flexible Array Members (C99) Special rules for structures that contain a flexible array member: –The flexible array must be the last member. –The structure must have at least one other member. Copying a structure that contains a flexible array member will copy the other members but not the flexible array itself. Copyright © 2008 W. W. Norton & Company. All rights reserved. 122

Chapter 17: 指针高级用法 Flexible Array Members (C99) A structure that contains a flexible array member is an incomplete type. An incomplete type is missing part of the information needed to determine how much memory it requires. Incomplete types are subject to various restrictions. In particular, an incomplete type can’t be a member of another structure or an element of an array. However, an array may contain pointers to structures that have a flexible array member. Copyright © 2008 W. W. Norton & Company. All rights reserved. 123