C程序设计与实践 第7章(课件)_第1页
C程序设计与实践 第7章(课件)_第2页
C程序设计与实践 第7章(课件)_第3页
C程序设计与实践 第7章(课件)_第4页
C程序设计与实践 第7章(课件)_第5页
已阅读5页,还剩97页未读 继续免费阅读

下载本文档

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

文档简介

程序设计与实践 目录第7章指针7 1概述7 2变量的指针和指针变量7 3函数与指针7 4数组与指针7 5字符串与指针7 6结构体与指针7 7动态存储管理7 8本章小结 第7章指针7 1概述前面我们已经学习过的数组类型 结构体类型和各种简单数据类型所表示的数据形式都是静态的 也就是说 程序在执行之前各项数据所需要的存储空间的大小是已知数 我们所希望的是能否在程序执行过程中 根据实际使用的需要 动态地使用内存空间 需要时使用 不需要时释放出来 换句话说 让内存公用 需要时占用 不需要时归还 针对这类普遍的问题 C语言提供了指针类型和内存动态管理的一整套有效机制 方便而高效地解决了这一类问题 为了更好地理解这些内容 我们需要从计算机存储结构说起 在计算机中处理的数据都要放在存储器中 存储器又分为内部存储器 就是通常所说的内存条 和外部存储器 硬盘 U盘 软盘等 计算机运行时 访问内存比访问外存速度快很多 但是同样容量的内存比外存的成本高很多 计算机内存是由线性连续的许许多多存储单元组成的 存储单元的最小单位是字节 每个字节可以分为字节内容和字节地址两个部分 字节内容由8个二进制位组成 可以表示0 255 共计256种不同信息 字节地址是这个字节的标识 而字节内容是通过字节地址来访问的 单个字节形式如图7 1所示 表示在地址为88886600的存储单元中存放了123这个数据 连续存储器单元的地址是连续编号的 如 88886600 88886601 88886602 数据在存储器中也是连续存放的 例如 字符串 hello 在存储区中存储形式如图7 2所示 每个字节内容是一个字母 第一个字母h保存在地址为x的单元中 第二个字母e保存在地址为x 1的单元中 以此类推 C语言中 由于不同数据类型的数据 变量和常量 所表示本类型不同数据的数量不同 因此该类型的数据所需占用的字节数可能不同 可以通过运算符sizeof 类型名 来获取各种类型数据的字节数 例7 1输出常用数据类型数据所占用的字节数 includevoidmain printf 一个字符型数据占 d个字节 n sizeof char printf 一个整数型数据占 d个字节 n sizeof int printf 一个浮点型数据占 d个字节 n sizeof float 程序运行结果为 一个字符类型数据占用1个字节一个整数类型数据占用4个字节 一个浮点类型数据占用4个字节我们也可以对构造类型用运算符sizeof来计算该类型数据所需的字节数 例7 2输出结构体数据类型的字节数 includevoidmain structdoc info charname 50 文档名charwriter 10 作者 intlength 文档大小chardatefirst 8 创建日期 格式yyyymmddchardatelast 8 最后修改日期 格式yyyymmddchardoctype 20 文档类别 a 10 printf 1个文档件的信息占用 d个字节 n sizeof structdoc info printf 10个文档的信息占用 d个字节 n sizeof a 程序运行结果分别是100和1000 说明每个文档的描述信息需要占用100字节的内存空间 在VC 6 0中整型数据占用4个字节 因此无论是整型变量或者是整型常量 都会占用4个字节的存储空间 而在计算机运行时 无论是对于变量值的访问 还是对于常量值的访问 都需要通过内存地址来访问 为了便于对地址的使用 C语言中引入了指针的概念 简单地理解 指针就是计算机内存的地址 7 2变量的指针和指针变量例7 3指针变量的定义与相关运算示例 includevoidmain inti 345 int pi 定义指向int型数据的指针变量pifloatf 1 24 float pf 定义指向float型数据的指针变量pf charch p char pch 定义指向char型数据的指针变量pch 并取变量ch的地址 赋值给pchpi i 取变量i的地址 赋值给pipf f 取变量f的地址 赋值给pfprintf i d pi d n i pi printf f 4 2f pf 4 2f n f pf printf ch c pch c n ch pch 程序运行结果 i 345 pi 345 f 1 24 pf 1 24ch p pch p程序说明 函数体中前六行是变量定义部分 特别注意的是在第2 4 6行出现的指针变量的定义 与一般变量的定义相比 指针变量的定义只需要在变量名前加上一个星号 另外 也可以在指针变量定义的同时为该变量赋初值 指针变量的值只能是一个地址 而 为取地址运算符 例如 在第六行中 char pch ch 表示定义一个指向char型数据的指针变量pch 并取变量ch的地址赋值给指针变量pch 此程序最后三行的输出函数printf 中 通过指针变量的值来访问它所 指向的内容 也就是对应的地址中的内容 例如 在输出函数调用printf i d pi d n i pi 中 pi是指针变量 pi的值为整型变量i的地址 pi就表示以指针变量pi的值为地址的内存中的值 内容 也就是说 因为pi的值为整型变量i的地址 所以 pi就表示变量i的值 它们的值相同 都是345 这里分别使用了直接访问和间接访问两种方式来输出变量i f ch的值 一 指针变量定义指针变量定义的一般写法是 数据类型 指针变量 指针变量2 其中 数据类型指的是指针变量指向数据的类型 可以是int char float等 指针变量写法上与其他变量命名方式相同 例如 p1 p2等 可以一次定义多个指向相同数据类型的指针变量 也可以每次只定义一个指针变量 例如 inti pi p1 定义i是整型变量 pi和p1分别是指向整数类型的指针变量int p2 定义p2是指向整数类型的指针变量变量的地址 每个变量在内存中占用若干个连续的字节 不同类型变量所占用的字节数不同 变量的地址就是为变量分配的连续存储空间中第一个空间的地址 二 取地址运算取地址运算的格式为 变量这里的变量可以是任何类型变量 在例7 3中 pi pf pch的结果 分别为变量i f ch的地址 三 间接访问运算 间接访问运算 的格式为 指针变量这里的指针变量必须是在程序中已经说明过的指针变量 并且也必须是已经被赋过值的 间接访问运算 的结果就是指针变量所指向的内存单 元中的值 例7 4取地址运算和间接访问运算举例 includevoidmain inta b c pa pb pc a 1 b 2 pa pc c pc pa pb printf c d n pc d n c pc 程序运行结果 c 3 pc 3思考 在程序中我们并没有给变量c赋值 为什么它的结果也是3呢 从图7 3中我们会看出 指针变量pc的值就是整型变量c的地址 而 pc就是c 因此 语句 pc pa pb c a b c pa pb 都是等价的 图7 3例7 4中各指针变量之间的关系四 指南针变量的赋值例7 5指针变量的赋值 includevoidmain inta b c pa pb pc a 2 b 5 pa 指针变量赋值的两种类型 pc pa pb printf c d n pc d n c pc 程序运行结果 c 4 pc 4 图7 4例7 5中各指针变量之间的关系五 需要特别注意的问题1 不要将未赋过值的指针变量用于间接访问如指针变量pa被定义以后 编译程序就为pa分配了内存空间 而空间中一定都会有数据 如果这时去使用间接访问 pa 也就是说将pa的值作为地址来使用 本身就是一件无意义的事 如果是继续执行下去 可能会损坏其他应用程序的程序或数据 严重的话 也可能会损坏系统程序的程序或数据 甚至于会导致整个系统瘫痪 后果是非常严重的 我们初学者一 定要特别小心 例7 6无效指针的使用 includevoidmain inti 345 int pi 定义指向int型数据的指针变量piprintf i d pi d n i pi 程序在执行到printf函数 输出指针变量pi所指的内容时 因为在此前pi并没有被赋值 所以pi的内容 pi也是无效的 因此该程序的执行会出现严重错误 图7 5和图7 6都是例7 6程序在VC 6 0环境中执行时的出错信息 图7 5无效指针的使用产生的出错信息图7 6无效指针的使用产生的出错信息 2 指针变量的值指针变量只能存放指针 地址 也不能用常量作为地址给指针变量赋值 例如 includevoidmain int pi pi 100200 printf d pi 或将对于pi的赋值改为pi int 100200 都是无意义的 3 指针变量只能存放与其定义时相同类型变量的地址例如有如下的变量定义 int pi float pf char pch 其中指针变量pi pf pch 分别只能接受int型 float型 char型变量的地址 否则出错 7 3函数与指针指针与函数有密切的关系 经常使用的场合有 1 在函数调用时 指针 指针变量或变量的指针 可以作为实际参数传递 2 在函数定义时 可以定义指针类型的形式参数 3 在函数定义时 可以定义指针类型的函数返回结果 一 指针作为函数调用的实际参数的用法平时我们把指针作为函数调用的实际参数有两种用法 第一种是变量的地址作为实际参数 第二种是指针变量作为实际参数 1 变量的地址作为实际参数变量的地址作为实际参数使用 最典型例子就是 intn scanf d n 这种用法我们非常熟悉 现在我们也知道了 n的用法 实际上在调用函数scanf 时 函数scanf 的第二个参数就是指针 地址 传递 传递的是变量n的地址 2 指针变量作为实际参数按上述说明进行推论 我们可以尝试以下方法 intn pn n scanf d pn 先定义了一个指针变量pn 并将变量n的地址赋给pn 调用函数scanf 的执行结果就是从键盘上读入一个整数值 保存在变量n中 从功能上它与 前一段程序是等价的 但是要特别注意 这时的指针变量pn一定是有确定地址的 二 指针类型的形式参数用法例7 7从键盘输入3门课程的成绩 按从大到小顺序输出 要求排序过程使用函数 解题思路 本题实质上是三个数的排序问题 而且要求是降序排列 我们采用最简单的排序方法 一边输入数据一边排序 先输入两个数据 将其按从大到小的次序排好 再输入第三个数据 将其分别与第一个数据和第二个数据分别进行排序 其中三次用到两个数的排序的操作 我们将它定义为一个函数order 来实现 include voidmain voidorder intp1 intp2 函数order的声明inta b c scanf d a scanf d b order a b scanf d c order a c order b c printf d d d n a b c voidorder intp1 intp2 交换两个参数的值 intt if p1 p2 t p1 p1 p2 p2 t 输入数据 708090输出结果 708090问题出在什么地方呢 问题就出在函数上 函数调用时 我们只是把变量a b c的值传递给了函数order 的参数p1和p2 次序调整只是影响到了p1和p2 对于a b c根本没有影响 所以a b c输出的结果仍然是原来的值 如果想在调用排序函数order 后使得a b c的大小也随之变化 解决办法就是将a b c的地址传递给函数order 因此要求函数order 的形式参数也必须是地址 指针类型的 修改后的程序如例7 8所示 例7 8对于例7 7程序的更正 includevoidmain voidorder int p1 int p2 inta b c scanf d a scanf d b order a b scanf d c order a c order b c printf d d d n a b c voidorder int p1 int p2 intt if p1 p2 t p1 p1 p2 p2 t 输入数据 708090输出结果 908070C语言中函数参数的传递只有一种形式 就是值传递 归纳起来就是非地址值和地址值两种 在使用时应该如何把握选择传递哪种值呢 只需要把握一个原则 如果在函数调用结束后 需要改变实际参数变量值 则需要实际参数值为变量的地址 否则实际参数值为非地址 例如 scanf d d a b 实际参数值为变量a和b的地址order a b 实际参数值为变量a和b的地址printf d d d a b a b 实际参数值为变量a和b的值和表达式的值一般情况下 如果实参传递是地址值 对应的形式参数应该是指针类型变量 如果实参传递是数值 变量 常量或表达式 对应的形式参数应该是对应类型的变量 三 函数返回指针类型的结果例7 9输入两个数 输出较大数 要求定义并使用一个函数来返回较大数的地址 程序代码 includevoidmain int pmax int p1 int p2 inta b pm scanf d d a b pm pmax a b printf a d b d max a b d n a b pm int pmax int p1 int p2 return p1 p2 p1 p2 输入数据 3553输出结果 a 35 b 53 max a b 53程序分析和说明 本程序的关键部分是函数pmax 的设计 函数pmax 的功能是返回两个整数中比较大的数的地址 经分析得知 函数pmax 要返回的结果应该 是指针类型 函数通过参数先获取两个要比较的整数的地址 这可以通过把函数pmax 形式参数定义为指针类型来解决 函数体就是一条语句 直接返回条件表达式的值 根据两个指针所指向的内容值的大小 结果是指向比较大的值的指针 返回指针类型结果的函数定义的一般形式 数据类型 函数名 形式参数表 return 指针类型表达式 函数体中必须要有return语句 必须要保证在任何情况下从函数返回时有指针类型的结果被返回 7 4数组与指针一 数据和指针的关系数组的每个元素都有对应的地址 因此也可以用指针来指向一个数组元素 例如 有如下程序段inta 6 1 2 3 4 5 6 int p p a 0 图7 7数组元素与指针的关系 程序经过编译后 数组被安排在连续的一块内存单元中 数组名就是这块内存单元的首地址 p a 0 与p a 是等价的每个数组都是由多个相同类型的数组元素组成的 每个数组元素占用相同字节数量的连续的存储空间 占用的字节数量取决于数组元素类型 数组元素的地址是指它所占有的几个内存单元的首地址 在编程时要特别注意的是p是变量 而a或 a 0 都是常量 还要注意的是指针变量指向的数据类型要与数组元素的数据类型相同 指针变量说明的一般形式也与普通指针类型说明形式相同 类型说明符 指针变量名 例7 10通过指针访问数组元素 includevoidmain inta 6 10 20 30 40 50 60 inti p for i 0 i 6 i printf d a i printf n for p a p a 6 p printf d p printf n 程序运行结果 102030405060102030405060程序说明 程序中有两个功能完全等价的for循环组成的程序段 第二个for循环控制方式和循环体中对于数组元素的使用都采用了与第一个循环语句中截然不同的方法 语句 for p a p a 6 p 循环控制用指针变量p 初值为数组名字a 也就是数组a的开始地址 或者说是a 0 的地址 循环控制变量p每次加1 使指针能够遍历到数组a的所有元素 循环结束条件为p a 6 此时循环结束条件p a 6已经不成立了 它相当于a 6 的地址 这对于数组a来说已经是下标超界了 根本无意义 循环体执行打印语句 printf d p 每次把当前指针所指的变量的值打印出来 从上面两个循环的比较中 我们发现第一种方法容易理解 而第二种方法更加简洁 这个例子对于我们深入理解数组与指针之间的关系很有帮助 二 数组名字与指针的应用数组名就是这个数组的首地址 也就是a 0 的地址 它们之间的关系如图7 8所示 图7 8数组名与数组的首地址之间的关系在程序中数组名字作为指针的使用方法如例7 11所示 例7 11数组名作为指针访问数组元素 includevoidmain inta 6 10 20 30 40 50 60 inti for i 0 i 6 i printf d a i printf n for i 0 i 6 i printf d a i printf n 程序运行结果 102030405060102030405060程序说明 为了方便比较 我们仍然采用对比方式 程序中的两个for循环组成的程序段功能仍然等价 两个for循环的控制方式相同 第二个for循环的循环体中对于数组元素的使用采用数组名作为指针访问数组元素的方法 语句 printf d a i 中 a i 与a i 是等价的 表示以a为基础地址 偏移i个数据元素 这种用法也可以用于指针变量上 如例7 12所示 例7 12用指针访问数组元素的两种方法 includevoidmain inta 6 10 20 30 40 50 60 inti p for p a p a 6 p printf d p printf n p a for i 0 i 6 i printf d p i printf n 程序运行结果 102030405060102030405060程序说明 程序中我们对于指针访问数组元素使用了两种方法 两个程序段功能相同 第一种方法已经在例7 10中详细地说明了 第二种方法采用指针和偏移量相结合来访问数组元素 两种方法使用的循环控制方式不同 循环体中对于数组元素的访问方式基本相同 都是采用指针间接访问方式 主要区别就在于指针变量本身变化与否 第二种方法中 在进入循环之前 指针变量p获取了数组a的首地址 在后面的使用中p本身一直没有发生改变 而是通过偏移量i的变化 并且通过p i的组合形成新的地址 来遍历数组的所有元素 综上所述 我们对于数组a的第i 1个元素 即下标为i的元素 i 0 可以有如下5种方法 1 数组元素 a i 2 数组名用作地址 a i 3 指针变量本身变化 p 4 指针变量不变偏移量改变 p i 5 指针变量作为数组名 p i 后面三种方法的应用如例7 13所示 例7 13指针变量访问数组元素的三种用法比较 includevoidmain inta 6 10 20 30 40 50 60 inti p for p a p a 6 printf d p printf n p a for i 0 i 6 printf d p i printf n for i 0 i 6 i printf d p i printf n 程序运行结果 102030405060102030405060102030405060上述三种方法中 采用第一种方法最简洁 执行的效率也最高 三 数组和指针作为函数参数的应用 数组和指针在使用时基本上是可以通用的 这在前面我们已经通过大量的例子了解的比较清楚了 那么作为函数的参数使用时数组名和指针仍然具有很强的通用性 例7 14定义一个函数 计算连续存放的10个整数之和 函数的形参为指针 然后在主程序中分别使用数组名和指针作为实参调用该函数 程序代码 includevoidmain intsump int p intn voidoutput inta intn int ptr array 10 1 2 3 4 5 6 7 8 9 10 ptr array output ptr 10 printf d n sump ptr 10 output array 10 printf d n sump array 10 intsump int p intn inti sum 0 for i 0 i n i sum sum p returnsum voidoutput inta intn inti sum 0 for i 0 i n i printf d a i printf b 程序运行结果图7 9例7 14程序的运行结果程序说明 程序由主函数和两个子函数 sump output 组成 程序功能是采用对比的方法 分别把数组名和指针变量作为实际参数来输出数组元素的值以及这些元素的和 函数output完成数组各元素的输出 函数sump计算数组元素之和 我们重点来关注两个函数对于数组名和指针变量作为实际参数的用法 为了清楚起见 我们把程序中数组和指针作为形参和实参使用方法的对照关系归纳总结出来形成了表7 1 从表中和程序中我们都可以看出当形式参数是指针时 实际参数可以是指针 也可以是数组 当形式参数是数组时 实际参数可以是指针 也可以是数组 虽然 调用方式不同 但是程序执行的结果完全相同 表7 1数组和指针作为形参和实参使用方法对照表 函数output中最后一条语句printf b 的作用是把已经输出的最后一个数后面跟随的 抹掉 因为在函数output中 循环输出了n个数 同时也输出了n个 但是我们只需要n 1个 最后的这个位置应该用 还可以考虑用其他的方法进行处理 四 特别注意的几个问题a 是错误的用法 因为a不是指针变量 它是数组的首地址 是指针常量 指针变量可以指到数组内存区以前或以后的内存单元 但是对于这些地址元素的访问系统并不认为非法 这是很危险的 注意 p 与 p 的差别 若p的初值为a 则 p 表示当前取a 0 的值以后 指针p增1 也就是数组元素a 1 的位置 而 p 则表示当前取a 0 的值以后 a 0 的值加1 参照图7 7 对于表达式p i 其中p是指针变量 i是整型变量 i值为正数 指针p向后 右 移动 i值为负数 指针p向前 左 移动 7 5字符串与指针在前面章节中我们学习过用数组来存放字符串 上一节我们又学习了数组和指针变量之间的紧密关系 在C语言中 字符串还有一些其他的特殊性 比如存放字符串的数组可以直接输入或输出 例7 15字符串的输入和输出 includevoidmain charstr 50 scanf s str printf s n str 在程序中str这个数组可以作为一个整体来使用 直接进行输入和输出 这与其他类型元素的数组的使用有着巨大差别 上节我们描述的指针对于数组元素操作的各种方法 对于字符串来说都适用 数组与字符串在使用上的主要差别在于 数组是通过控制下标来访问数组的所有元素的 字符串的全部字符都存在数组中 但是字符串的结束标识为特殊字符 0 如果定义一个字符数组s 50 它可以保存50个任何字符 也可以保存任何小于50个字符的字符串 因为字符串的结束标识 0 也需要占用一个位置 例7 16字符数组和字符串指针 includevoidmain chars1 我喜欢C语言 char s2 字符串很好用 printf s n s n s1 s2 运行结果 我喜欢C语言 字符串很好用 程序说明 char s2 字符串很好用 表示s2是一个指向字符串的指针变量 把字符串的首地址赋予s2 使用字符串指针变量与字符数组是有区别的 程序中的 char s2 字符串很好用 等效于 char s2 s2 字符串很好用 但是 语句chars1 我喜欢C语言 不能等效于chars1 20 s1 我喜欢C语言 不能用字符串对数组名直接赋值 可以通过库函数来实现字符串的复制 例如 strcpy s1 我喜欢C语言 例7 17指针与字符串的应用 编写程序连接两个已有的字符串成为一个新的字符串 include includevoidmain voidmystrcat char ps char ps1 char ps2 chars 40 s1 20 char s2 strcpy s1 我喜欢C语言 s2 字符串真好用 printf 字符串1 s n字符串2 s n s1 s2 mystrcat s s1 s2 printf 字符串1与字符串2连接结果 s n s voidmystrcat char ps char ps1 char ps2 将字符串ps1与字符串ps2的连接 并将结果保存在字符串ps中 while ps ps1 ps while ps ps2 程序运行结果 字符串1 我喜欢C语言 字符串2 字符串真好用 字符串1与字符串2连接结果 我喜欢C语言 字符串真好用 程序分析说明 主函数中 定义了字符型数组s1用于存放第一个字符串 定义了字符类型的指针s2用于指向第二个字符串 定义了字符型数组s用于存放两个字符串连接后的结果字符串 语句部分中 首先调用库函数strcpy 完成了对于s1的赋值 然后字符串常量 字符串真好用 直接赋值给字符指针s2 也 就是将该字符串常量的首地址赋值给了字符指针s2 然后输出s1和s2的当前值 接下来调用函数mystrcat 完成将字符串s1与字符串s2的连接 并将结果保存在字符型数组s中 最后将连接后的结果输出 子函数mystrcat 定义了三个字符指针类型的形式参数ps ps1和ps2 分别用于指向连接结果 第一个字符串和第二个字符串 在函数体部分共有三个语句 第一个while型循环语句 当 ps1 指针ps1指向的字符 不是0 0 时 连续复制单个字符 两个字符指针分别加1 分别向后移动一个位置 最终将第一个字符串中的全部字符都送到了目的位置 注意 当 ps1为 0 时 已经执行了 ps ps1 所以要将指针回退一个位置 即语句ps 下一次第二个字符串中的第一个字符将会占用这个位置 在这里即使第二个字符串是空字符串也不会出现问题 第二个while型循 环语句 与第一个while型循环语句结构相同 最终完成将第二个字符串中的全部字符都送到ps中指定位置 不要担心 在ps指向的连接结果字符串的最后已经加上字符串的结束标识 0 了 7 6结构体与指针指向结构体变量的指针例7 18指向结构体变量指针的应用实例 include includevoidmain structdoc info type charname 50 文档名 charwriter 10 作者intlength 文档大小chardatefirst 9 创建日期 yyyymmddchardatelast 9 最后修改日期 yyyymmddchardoctype 21 文档类别 structdoc info typemydocinfo C语言程序设计 余江等 654321 20080808 20100606 教材 structdoc info type pdocinfo pdocinfo mydocinfo printf 资料信息列表 n n printf 文档名 s n mydocinfo name printf 作者 s n mydocinfo writer printf 文档大小 d字节 n pdocinfo length printf 创建日期 yyyymmdd s n pdocinfo datefirst printf 最后修改日期 yyyymmdd s n pdocinfo datelast printf 文档类别 s n pdocinfo filetype printf n 程序运行结果 资料信息列表 文档名 C语言程序设计作者 余江等文档大小 654321字节创建日期 yyyymmdd 20080808最后修改日期 yyyymmdd 20100606文档类别 教材 程序分析说明 程序的说明部分 我们先定义了一个结构体数据类型doc info type 它是由name等6个字段构成 然后分别定义了一个该结构体类型的变量mydocinfo和一个指向该结构体类型的指针变量pdocinfo 为了简化程序 我们为变量mydocinfo赋了初值 程序的语句部分 我们主要做了以下几件事情 为指针变量pdocinfo赋值 使该指针指向结构体变量mydocinfo的首地址 以 结构体变量名 字段名 的形式 分别输出了mydocinfo name和mydocinfo writer的值 以 结构体指针变量名 字段名 的形式 分别输出了 pdocinfo length和 pdocinfo datefirst的值 以 结构体指针变量名 字段名 的形式 分别输出了pdocinfo datelast和pdocinfo doctype的值 在输出的格式上做了一些简单设置 注意事项 1 在使用 结构体指针变量名 字段名 形式时 括号是一定不能省略的 2 为了方便字符串的操作 表示日期的字段datefirst和datelast都定义为长度是9的字符数组 从以上例子我们可以看出 指向结构体变量的指针 获取结构体变量地址的方式与其他类型指针的用法相同 但是对于结构体变量内容的 访问 可以有两种方式 1 结构体指针变量名 字段名 2 结构体指针变量名 字段名实际工作中使用较多的是第2种形式 它更加简洁直观 指向结构体数组的指针例7 19指向结构体数组的指针应用实例 include include defineLISTLEN20 voidmain structdoc info type charname 50 文档名charwriter 10 作者intlength 文档大小chardatefirst 9 创建日期 yyyymmddchardatelast 9 最后修改日期 yyyymmddchardoctype 21 文档类别 structdoc info typedoc info list LISTLEN structdoc info type p 数据输入部分for p doc info list pname printf 作者 scanf s p writer printf 文档大小 字节 scanf d p length printf 创建日期 yyyymmdd scanf s p datefirst printf 最后修改日期 yyyymmdd scanf s p datelast printf 文档类别 scanf s p doctype printf n 数据输出部分printf 资料信息列表 n n for p doc info list p doc info list LISTLEN p printf 文档名 s n p name printf 作者 s n p writer printf 文档大小 d字节 n p length printf 创建日期 yyyymmdd s n p datefirst printf 最后修改日期 yyyymmdd s n p datelast printf 文档类别 s n p doctype printf n 程序说明 程序的功能是读入多个文档的信息 存入到结构体类型元素的数组中 然后再依次输出出来 结构体类型指针变量的定义 structdoc info type p 多个文档的信息的输入和输出 我们分别用循环来控制 用指针p作为循环控制变量 初值取数组的首地址 每循环一次指针后移一个元素的位置 根据数组长度来判断是否结束循环 scanf d p length 表示 读入一个整数 保存到指针指向的当前元素的length字段中 length是整数类型 因此在输入时要提供变量的地址 但不能单独写成 length 输入和输出函数中对于其他数据字段的访问 都是采用 指针变量 字段名 这种形式来实现的 结构体指针变量作函数参数例7 20指向结构体变量指针的应用实例 include includestructdoc info type charname 50 文档名charwriter 10 作者intlength 文档大小chardatefirst 9 创建日期 yyyymmdd chardatelast 9 最后修改日期 yyyymmddchardoctype 21 文档类别 voidmain voidmy print structdoc info type ptr structdoc info typedoc info C语言程序设计 余江等 654321 20080808 20100606 教材 printf 资料信息列表 n n my print doc info voidmy print structdoc info type p printf 文档名 s n p name printf 作者 s n p writer printf 文档大小 d字节 n p length printf 创建日期 yyyymmdd s n p datefirst printf 最后修改日期 yyyymmdd s n p datelast printf 文档类别 s n p doctype printf n 程序运行结果 资料信息列表 文档名 C语言程序设计作者 余江等文档大小 654321字节创建日期 yyyymmdd 20080808 最后修改日期 yyyymmdd 20100606文档类别 教材程序分析说明 程序的功能与例7 18相同 也是打印文档信息表 在主函数外面我们定义了结构体数据类型doc info type 因为主函数和子函数定义变量时都要用到这个类型 文档信息打印工作由子函数my print 来完成 它只有一个形参p 是结构体类型doc info type的指针 主函数中只有两个语句 第一个语句printf 完成打印表头部分 注意这个语句不能安排在子函数my print 中 否则 连续调用my print 打印多个文档的信息时 将会打印出多个表头 第二个语句调用子函数 my print doc info 完成打印出变量doc info中信息的工作 注意 因为my print 的形参p是结构体类型doc info type的指针 所以在调用该函数时所提供的实参也应该是结构体类型doc info type变量的地址 7 7动态存储管理一 动态存储分配应用举例在实际开发工作中 动态存储分配是经常使用的 例如 我们要把多个字符串连接到一起 连接后的结果需要占用多少空间 事先我们无法估算 预留空间少了不够用 多了是浪费 要做到恰到好处只能是动态分配空间 我们对于问题简化一下 在例7 17的基础上做一些修改 得到例7 21 例7 21动态存储管理的应用实例 编写程序连接两个已有的字符串成为一个新的字符串 新的字符串所需要的存储空间要根据两个已有的字符串的大小动态产生 include include includevoidmain voidmystrcat char PS12 char ps1 char ps2 chars1 1000 s2 1000 s12 printf 请输入字符串1 scanf s s1 printf 请输入字符串2 scanf s s2 s12 char malloc strlen s1 strlen s2 1 mystrcat s12 s1 s2 printf 字符串1与字符串2连接结果 s n s12 free s12 voidmystrcat char ps12 char ps1 char ps2 while ps12 ps1 ps12 while ps12 ps2 程序分析说明 主函数中 定义了两个容量较大的字符数组s1和s2 还定义了字符类型的指针s12用于存放两个字符串连接后的新字符串 注意这时只是指针变量s12本身有存储空间了 而用于存放连接结果的字符串暂时还没有存储空间 语句部分 首先分别从键盘上读入两个字符串的值 然后 也是最关键的地方 调用库函数malloc 申请内存空间 malloc 的参数是要申请内存的数量 它是以字节为单位的 可以通过另外一个库函数strlen 分别计算出字符串s1和s2的长度 两者之和再加1 就是连接后字符串所需要空间 的字节数 加1是为字符串的结束标识 0 预留空间位置 函数malloc 的返回结果是指针类型 这里用 char 做强制类型转换 结果值就是申请到的空间的首地址 存入变量指针变量s12中 接下来调用函数mystrcat 完成将字符串s1与字符串s2的连接 并将结果保存在字符指针s12所指向的空间中 然后将s12的结果输出 最后 释放掉s12指向的内存块所占用的存储空间 关于子函数mystrcat的说明 参见例7 16程序说明的相关部分 二 动态内存管理的库函数常用的内存管理函数有三个 内存分配函数malloc calloc和内存释放函数free 对这三个函数的使用需要加入 include 内存

温馨提示

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

评论

0/150

提交评论