




已阅读5页,还剩226页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
C语言程序设计教程 三 第8章指针第9章复合数据类型 第8章指针 C语言的重要特性之一就是指针操作 利用指针可以直接而快速的处理内存中各种数据结构的数据 特别是数组 字符串 内存的动态分配等 并能象汇编语言一样处理内存地址 它为函数间各类数据的传递提供了简捷便利的方法 指针使C程序简洁 紧凑 高效 但指针使用上的灵活性容易导致指针滥用而可能使程序失控 本章主要介绍如下内容 指针的概念和运算 指针和数组 指针函数和函数指针 多级指针以及指针的实例举例 8 1指针基本概念及其指针变量的声明 在C语言中 对于变量的访问形式之一 就是先求出变量的地址 然后再通过地址对它进行访问 这就是本节所要论述的指针及其指针变量 8 1 1指针 变量的指针和指针变量 例8 1指针及其指针变量应用举例 includevoidmain inti 20 x int ip ip 8 1 1指针 变量的指针和指针变量 续 运行结果 i saddressis 1310588ip saddressis 1310588i sdatais 20ip sdatais 20 x 20现在我们对例8 1进行代码分析 逐步深入了解什么是指针及其指针变量的应用意义 inti 20 x int ip 声明了两个整型变量i x 并对整型变量i进行初始化 赋初值为20 同时还声明了一个指向整型数的指针变量ip i x中可存放整数 而ip中只能存放整型变量的地址 我们把i的地址赋给ip 即 ip 8 1 1指针 变量的指针和指针变量 续 此时指针变量ip指向整型变量i 由运行结果可知变量i的地址为1310588 该地址是在64位计算机上运行得出的结果 不同的计算机得出的地址不同 这个赋值可形象理解为图8 1所示的联系 8 1 1指针 变量的指针和指针变量 续 以后我们便可以通过指针变量ip间接访问变量i 例如 x ip 运算符 访问以ip为地址的存贮区域 而ip中存放的是变量i的地址 因此 ip访问的是地址1310588的存贮区域 因为是整数 实际上是从1310588开始的两个字节 它就是i所占用的存贮区域 所以上面的赋值表达式等价于 x i 所谓变量的指针 实际上指变量的地址 变量的地址虽然在形式上好像类似于整数 但在概念上不同于以前介绍过的整数 它属于一种新的数据类型 即指针类型 8 1 1指针 变量的指针和指针变量 续 在C语言中 一般用 指针 来指明这样一个表达式 x的类型 而用 地址 作为 x的值 也就是说 若x为一整型变量 则表达式 x的类型是指向整数的指针 而 x的值是变量x的地址 同样 若doubled 则 d的类型是指向双精度数d的指针 而 d的值是双精度变量d的地址 严格地说 一个指针就是一个地址 是一个常量 而一个指针变量却可以被赋予不同的指针值 是变量 但常把指针变量简称为指针 为了避免混淆 我们约定 指针 是指地址 是常量 指针变量 是指取值为地址的变量 声明指针的目的是为了通过指针去访问内存单元 8 1 2指针的声明 在 语言中 变量的地址是由编译系统分配的 对用户完全透明 用户不知道变量的具体地址 下面介绍两个与地址有关的运算符 取地址运算符 指针运算符 或称 间接访问 运算符 语言中提供了地址运算符 来表示变量的地址 其一般形式为 变量名或数组元素名 如 a表示变量a的地址 b 2 表示数组元素b 2 的地址 变量本身必须预先说明 设有指向整型变量的指针变量p 如要把整型变量a的地址赋予p可以有以下两种方式 8 1 2指针的声明 续 1 指针变量初始化的方法inta int p C语言中另一种与地址有关的运算符是 其一般形式为 指针变量名或目标变量地址 运算符要求运算量是地址 含义为访问指定地址的目标变量 8 1 2指针的声明 续 例如 px 含义为取指针px的目标变量的地址 就是px x 含义为访问变量x的地址指向的目标变量 就是x 可见 运算和 运算互为逆运算 指针变量同普通变量一样 使用之前不仅要声明说明 而且必须赋予具体的值 未经赋值的指针变量不能使用 否则将造成系统混乱 甚至死机 指针变量的赋值只能赋予地址 决不能赋予任何其它数据 否则将引起错误 指针变量的一般声明为 类型标识符 标识符 其中标识符是指针变量的名字 标识符前加了 号 表示该变量是指针变量 而最前面的 类型标识符 表示该指针变量所指向的变量的类型 8 1 2指针的声明 续 对于外部或静态指针变量在声明中若不带初始化项 指针变量被初始化为NULL 它的值为0 在C语言中规定 当指针值为零时 指针不指向任何有效数据 有时也称该指针为空指针 因此 当调用一个要返回指针的函数时 以后会讲到 常使用返回值为NULL来指示函数调用中某些错误情况的发生 既然在指针变量中只能存放地址 那么在使用中就不能将一个整数值赋给一个指针变量 下面的赋值是不合法的 int ip ip 100 另外 指针变量和一般变量一样 存放在它们之中的值是可以改变的 也就是说可以改变它们的指向 8 1 2指针的声明 续 例8 2指针应用举例 includevoidmain boolFlag true charn m p1 p2 n a m b p1 8 1 2指针的声明 续 if Flag p2 p1 printf p2 p1 n c m c p2 c p1 c n n m p2 p1 if Flag p2 p1 printf p2 p1 n c m c p2 c p1 c n n m p2 p1 8 1 2指针的声明 续 由上述程序描述 建立如图8 2所示的联系 8 1 2指针的声明 续 当Flag true时 这时赋值表达式 p2 p1 a p2m b p1n图8 3p2 p1时的情形就使p2与p1指向同一对象n 此时 p2就等于n 而不是m 图8 2 就变成图8 3所示 8 1 2指针的声明 续 运行结果 n a m b p1 a p2 bp2 p1 n a m b p2 a p1 a 当Flag false时 执行如下表达式 p2 p1 则表示把p1指向的内容赋给p2所指的区域 此时图8 2就变成图8 4所示 8 1 2指针的声明 续 运行结果 n a m b p1 a p2 b p2 p1 n a m a p2 a p1 a通过指针访问它所指向的一个变量是以间接访问的形式进行的 所以比直接访问一个变量要费时间 而且不直观 因为通过指针要访问哪一个变量 取决于指针的值 即指向 例如 p2 p1 实际上就是 m n 前者不仅速度慢而且目的不明确 但由于指针是变量 我们可以通过改变它们的指向 用以间接访问不同的变量 这就使程序代码编写得更为简洁 有效 指针变量也可以出现在表达式中 8 1 2指针的声明 续 例如 intx y px 相当于y px px 当指针作为函数参数传递时 参数传递的不是数据本身 而是数据的存储地址 因此 在形实结合传送地址时 要求实参和形参都是相同数据类型的地址量 例如 8 1 2指针的声明 续 例8 3指针作为函数参数传递的应用 includeintswap int iData1 ptr int iData2 ptr voidmain intiTest1 88 iTest2 66 printf 调用swap 前 iTest1 d iTest2 d n iTest1 iTest2 swap 8 1 2指针的声明 续 intswap int iData1 ptr int iData2 ptr intiTemp printf 调用swap 中交换前 iData1 ptr d iData2 ptr d n iData1 ptr iData2 ptr iTemp iData1 ptr iData1 ptr iData2 ptr iData2 ptr iTemp printf 调用swap 中交换后 iData1 ptr d iData2 ptr d n iData1 ptr iData2 ptr return0 8 1 2指针的声明 续 运行结果 调用swap 前 iTest1 88 iTest2 66调用swap 中交换前 iData1 ptr 88 iData2 ptr 66调用swap 中交换后 iData1 ptr 66 iData2 ptr 88调用swap 后 iTest1 66 iTest2 88在例8 3中 由于实参传递给形参的不是数据而是地址 被调函数swap 实际上是通过指针对调用函数中的变量iTest1 iTest2所存储的数据进行操作 操作的结果改变了调用函数中有关变量的数据 如 对变量iTest1 iTest2实行了数据交换 8 2地址运算 指针允许的运算方式有 1 指针在一定条件下可进行比较这里所说的一定条件是指两个指针指向同种类型对象才有意义 例如两个指针变量p q指向具有同种数据类型数组 则 等关系运算符都能进行正常运算 若p q为真 则表示p q指向数组的同一元素 若p q为真 则表示p所指向的数组元素在q所指向的数组元素之前 对于指向数组元素的指针在下面将作详细讨论 2 指针和整数可进行加 减运算设p是指向某一数组元素的指针 开始时指向数组的第0号元素 设n为一整数 则p n就表示指向数组的第n号元素 即下标为n的元素 不论指针变量指向何种数据类型 指针和整数进行加 减运算时 编译程序总根据所指对象的数据长度对n进行放大 在一般微机上 char放大因子为1 int short放大因子为2 long和float放大因子为4 double放大因子为8 对于下面章节所讲述到的结构或共用体 也仍然遵守这一原则 8 2地址运算 续 3 两个指针变量在一定条件下可进行减法运算设p q指向同一数组 则p q的绝对值表示p所指对象与q所指对象之间的元素个数 其相减的结果遵守对象类型的字节长度进行缩小的规则 注意 不同数据类型指针之间 指针与一般常数之间的关系运算是没有意义的 但是指针和零之间的相等与不相等运算可用于判断指针是否为空指针 如判断指针p是否为空指针 可用p 0或p 0来判断 也可以用p NULL或p NULL来判断 例8 4指针的应用 地址运算 include 8 2地址运算 续 voidmain intiArray 10 int iPoint1 iPoint2 intiNumber printf 数组iArray的初始化值 for inti 0 i 10 i iArray i i if i 5 0 printf n printf iArray d d t i iArray i printf n iPoint1 8 2地址运算 续 指针比较运算 printf 指针比较运算 n printf iPoint1 d tiPoint2 d n iPoint1 iPoint2 printf iPoint1 d tiPoint2 d n iPoint1 iPoint2 if iPoint1 iPoint2 printf iPoint1 iPoint2 n elseif iPoint1 iPoint2 printf iPoint1 iPoint2 n elseprintf iPoint1 iPoint2 n printf n 指针与整数相加运算 printf 指针与整数相加运算 n printf iPoint1 d tiPoint2 d tiNumber d n iPoint1 iPoint2 iNumber printf iPoint1 d tiPoint2 d tiNumber d n iPoint1 iPoint2 iNumber 8 2地址运算 续 iPoint1 iPoint1 iNumber iPoint2 iPoint2 iNumber printf iPoint1 iPoint1 iNumber iPoint1 d n iPoint1 printf iPoint1 iPoint1 iNumber iPoint1 d n iPoint1 printf iPoint2 iPoint2 iNumber iPoint2 d n iPoint2 printf iPoint2 iPoint2 iNumber iPoint2 d n iPoint2 printf n 指针减法运算 printf 指针减法运算 n iNumber iPoint1 iPoint2 printf iPoint1 d tiPoint2 d n iPoint1 iPoint2 printf iNumber iPoint1 iPoint2 iNumber d n iNumber printf n 8 2地址运算 续 运行结果 数组iArray的初始化值 iArray 0 0iArray 1 1iArray 2 2iArray 3 3iArray 4 4iArray 5 5iArray 6 6iArray 7 7iArray 8 8iArray 9 9指针比较运算 iPoint1 1310552iPoint2 1310572iPoint1 0iPoint2 5iPoint1iPoint1 1310564iPoint1 iPoint1 iNumber iPoint1 3iPoint2 iPoint2 iNumber iPoint2 1310584iPoint2 iPoint2 iNumber iPoint2 8指针减法运算 iPoint1 1310564iPoint2 1310584iNumber iPoint1 iPoint2 iNumber 5 8 3指向数组元素的指针 例8 5数组元素的等价引用 includevoidmain int iPoint intiNum 5 iPoint iNum 建立指针和数组关联 for inti 0 i 5 i iPoint i i 1 用指针地址法引用数组元素 for i 0 i 5 i printf iNum d d t i iNum i 用下标法引用数组元素 printf n 运行结果 iNum 0 1iNum 1 2iNum 2 3iNum 3 4iNum 4 5 8 3指向数组元素的指针 续 下面我们通过上例来详细说明指针与数组的关系 首先声明一个整型数组和一个指向整型的指针变量 int iPoint intiNum 5 和前面介绍过的方法相同 可以使整型指针iPoint指向数组中的任何一个元素 假定给出赋值运算 iPoint 8 3指向数组元素的指针 续 这里我们用第二种方法建立指针iPoint与数组iNum的关联 由上例结果我们知道 当指针与数组建立关联以后 对指针的操作就是对关联数组的操作 下面我们用指针给出数组元素的地址和内容的几种表示形式 1 根据地址运算规则 arry 1为arry 1 的地址 arry i就为arry i 的地址 同理 p i和arry i均表示arry i 的地址 或者说 它们均指向数组第i号元素 即指向arry i 2 p i 和 arry i 都表示p i和arry i所指对象的内容 即为arry i 3 指向数组元素的指针 也可以表示成数组的形式 也就是说 它允许指针变量带下标 如p i 与 p i 等价 假若 p arry 5 则p 2 就相当于 p 2 由于p指向arry 5 所以p 2 就相当于arry 7 而p 3 就相当于 p 3 它表示arry 2 8 3指向数组元素的指针 续 例8 6输入10个数 存入data数组 求出这10个数据的最大值和最小值 includevoidmain float fPoint fPend floatfData 10 floatfMax fMin fPoint fData 建立指针和数组的关联 fPend fData printf Enter10number n for inti 0 i 10 i scanf f fPend fPend 结果为地址 依次指向各个数组元素 循环结束后 fPend指向数组末尾元素fData 9 之后 fMax fData 先假定fData 0 为最大值和最小值 fMin fData 8 3指向数组元素的指针 续 循环中 fPoint的指向从fData 1 直至fData 9 for fPoint fData 1 fPointfMax fMax fPoint elseif fPoint fMin fMin fPoint printf Max f tMin f n fMax fMin 运行结果 Enter10number 4 26 3 1 55 543 53 72 7 2 88 6 Max 8 600000Min 2 800000 8 3指向数组元素的指针 续 例8 7求数组平均值 用形实结合传送数组首地址方式传送数组数据 include defineN5floatmean int data intn 形参data为指针变量 floataver 0 0 for inti 0 i n i data 求和 aver data aver n 求平均值 returnaver 返回平均值 8 3指向数组元素的指针 续 voidmain inta N floatav printf 输入数组元素 n for inti 0 i N i 输入数组元素值 scanf d 输出结果 运行结果 输入数组元素 10 31520 725 6 Mean 15 4 8 3指向数组元素的指针 续 在例8 7中 用形实结合传送地址的方式将数组名a作为实参传递给形参data 使指针变量data指向数组a的首地址 这样 被调函数mean 中 data的操作实际上就是对调用函数中的a数组元素操作 通常数组含有众多元素 不可能将他们一并作为数组传送给被调函数 只有采取传送数组首地址的方式 使得被调函数和调用函数都能对同一数组数据空间操作 这样就解决了大批量数据在函数调用时互相传递的问题 8 3指向数组元素的指针 续 例8 8求已知数组中的最大值元素 并将它移到该数组最前面 其余元素顺序后移 用形实结合传送数组首地址方式传送数组数据 include defineN5intMaxData intiData intiPosition intMoveData int iData intiPosition voidmain inta N m printf 移动前数组元素 8 3指向数组元素的指针 续 for inti 0 i N i 输入数组 a i i printf d t a i printf n m MaxData a N 调用MaxData 函数 得到最大元素下标值 MoveData a m 调用MoveData 函数 完成要求的移动 printf 移动后数组元素 for i 0 i N i 输出移动后的数组 printf d t a i printf n 8 3指向数组元素的指针 续 定义求最大元素下标值函数 形参iData 为虚数组首指针 intMaxData intiData intiPosition intmax mi max iData 0 for inti 1 i iPosition i 求最大元素下标 if max iData i mi i 记下最大的元素下标 max iData i returnmi 返回最大元素下标值 8 3指向数组元素的指针 续 定义完成移动最大元素在数组最前面 其余元素顺序后移的函数 形参iData为指针 准备接受数组首地址 intMoveData int iData intiPosition intmx mx iData iPosition mx暂存最大元素值 for inti iPosition i 0 i 顺序后移 iData i iData i 1 iData 0 mx 最大元素放在最前面 return0 运行结果 移动后数组元素 01234移动后数组元素 40123 8 4二维数组元素的地址 例8 9输出二维数组的数组元素值 includevoidmain intarry 3 4 1 2 3 4 11 12 13 14 21 22 23 24 for inti 0 i 3 i for intj 0 j 4 j printf 4d arry i j 输出数组元素arry i j printf n 8 4二维数组元素的地址 续 运行结果 12341112131421222324通过例8 9我们来说明问题 首先声明一个二维数组 如下所示 intarry 3 4 1 2 3 4 11 12 13 14 21 22 23 24 arry为二维数组名 此数组有3行4列 共12个元素 换句话说 数组arry是由三个元素组成 arry 0 arry 1 arry 2 而每个元素又是一个一维数组 且都含有4个元素 相当于二维数组的列 8 4二维数组元素的地址 续 例如 arry 0 所代表的一维数组包含的4个元素为 arry 0 0 arry 0 1 arry 0 2 arry 0 3 如图8 5所示 arry 8 4二维数组元素的地址 续 但从二维数组的角度来看 arry代表二维数组的首地址 当然也可看成是二维数组第0行的首地址 arry 1就代表第1行的首地址 arry 2就代表第2行的首地址 如果此二维数组的首地址为1000 由于第0行有4个整型元素 所以arry 1为1016 arry 2也就为1032 如图8 6所示 8 4二维数组元素的地址 续 既然把arry 0 arry 1 arry 2 看成是一维数组名 可以认为它们分别代表它们所对应的数组的首地址 也就是说 arry 0 代表第0行中第0列元素的地址 即 arry 0 0 arry 1 是第1行中第0列元素的地址 即 arry 1 0 根据地址运算规则 arry 0 1即代表第0行第1列元素的地址 即 arry 0 1 一般而言 arry i j即代表第i行第j列元素的地址 即 arry i j 另外 在二维数组中 我们还可用指针的形式来表示各元素的地址 如前所述 arry 0 与 arry 0 等价 arry 1 与 arry 1 等价 因此arry i j就与 arry i j等价 它表示数组元素arry i j 的地址 因此 二维数组元素arry i j 可表示成 arry i j 或 arry i j 它们都与arry i j 等价 或者还可写成 arry i j 8 4二维数组元素的地址 续 例8 10另一种输出二维数组方法 includevoidmain intarry 3 4 1 2 3 4 11 12 13 14 21 22 23 24 int iPoint iPoint arry 0 while iPoint arry 0 12 printf iPoint arry 0 1 4 4d 4d n iPoint 8 4二维数组元素的地址 续 运行结果 12341112131421222324 8 5指向一个由n个元素所组成的数组指针 例8 11输出二维数组任意行任意列元素值 includevoidmain intarry 3 4 1 2 3 4 11 12 13 14 21 22 23 24 int iPoint 4 声明iPoint是一个指向包含4个元素的一维数组的指针 intiRow iLine iPoint arry iPoint作为行指针 printf EnterRow Line scanf d d 8 5指向一个由n个元素所组成的数组指针 续 运行结果 EnterRow Line 2 3arry 2 3 24分析例8 11 声明指针变量如下 int iPoint 4 指针iPoint表示为指向一个由4个元素所组成的整型数组的指针 在声明中 是不能缺少的 否则它是指针数组 而不是数组指针 这种数组的指针不同于前面介绍的整型指针 当整型指针指向一个整型数组元素时 进行指针 地址 加1运算 表示指向数组的下一个元素 此时地址值增加了4 因为整型数据类型在VC 6 0中占4个字节 既放大因子为4 而如上所声明的是指向一个由4个元素组成的数组的指针 进行地址加1运算时 其地址值增加了16 放大因子为 这种数组指针在C语言中很少使用 但在处理二维数组时 还是比较方便的 例如 intarry 5 5 p 5 8 5指向一个由n个元素所组成的数组指针 续 p arry 开始时p指向二维数组第0行 当进行p 1运算时 根据地址运算规则 此时放大因子为 所以此时正好指向二维数组的第1行 和二维数组元素地址计算的规则一样 p 1指向arry 0 1 p i j则指向数组元素arry i j 例8 12数组指针的应用 includeintarry 5 5 2 4 6 8 10 10 12 14 16 18 18 20 22 24 26 26 28 30 32 34 34 36 38 40 42 8 5指向一个由n个元素所组成的数组指针 续 voidmain inti Point 5 for i 0 i 4 i Point arry i printf d t Point i 2 printf n 运行结果 622380 8 6字符指针 我们已经知道 字符串常量是由 括起来的一组字符序列 例如 astring 就是一个字符串常量 该字符串中因为字符a后面还有一个空格字符 所以它是由8个字符序列组成 在程序中如果出现字符串常量 C编译程序就会给该字符串常量按排一连续存贮区域 这个区域是静态的 在整个程序运行的过程中始终占用 平时所讲的字符串常量的长度是指该字符串的字符个数 但在按排存贮区域时 C编译程序还自动给该字符串序列的末尾加上一个空字符 0 用来标志字符串的结束 因此一个字符串常量所占的存贮区域的字节数总比它的字符个数多一个字节 让字符指针与存放字符串的字符数组关联 就可以用字符指针表示该字符串 8 6字符指针 续 例8 13字符指针指向字符数组 includevoidmain charstr Goodmorning char chPoint chPoint str chPoint指向str数组的首地址 printf strstringis s n str printf 1 chPointstringis s n chPoint chPoint str 5 printf 1 chPointstringis s n chPoint 8 6字符指针 续 运行结果 strstringisGoodmorning 1 chPointstringisGoodmorning 1 chPointstringismorning 例8 13中 当chPoint指向str字符串首地址时 输出整个str字符串 当chPoint指向str字符串中某一个字符地址时 输出以该字符为首字符的后续字符串 因此 使用字符指针比使用字符数组名更为灵活 C语言中操作一个字符串常量的方法有如下两种 1 把字符串常量存放在一个字符数组中 例如 chars astring 8 6字符指针 续 数组s共由9个元素组成 其中s 8 中的内容是 0 实际上 在字符数组声明的过程中 编译程序直接把字符串复制到数组中 即对数组s初始化 2 用字符指针指向字符串 然后通过字符指针来访问字符串存贮区域 当字符串常量在表达式中出现时 根据数组的类型转换规则 它被转换成字符指针 因此 若我们声明了一个字符指针cp char cp 于是可用 cp astring 使cp指向字符串常量中的第0号字符a 以后我们可以通过cp来访问这一存贮区域 如 cp或cp 0 就是字符a 而cp i 或 cp i 就相当于字符串的第i号字符 但不能通过指针来修改字符串常量 8 6字符指针 续 例8 14把键盘输入的字符串逆序排列 然后输出 include includevoidmain charstr 80 t char chPoint1 chPoint2 printf Enterastring scanf s str 输入要处理的字符串 for chPoint1 str chPoint2 chPoint1 strlen str 1 chPoint1 chPoint2 chPoint1 chPoint2 t chPoint1 chPoint1 chPoint2 chPoint2 t printf Thereversedstringis s n str 8 6字符指针 续 运行结果 Enterastring Hello Thereversedstringis olleH字符串是特殊形式的字符数组 因此 如果要从调用函数传送字符串给被调函数处理 同样要采用字符串首地址作为实参的方式 当然对应的形参必须是char型指针或char型虚数组首指针 还有一个特别之处 字符串常量也可以作为实参 此时 表面上接受它的形参是char地址量 实际上是让此形参指向该字符串的首地址 因而被调函数同样可以对这个字符串常量所占据的空间操作 8 6字符指针 续 例8 15字符串在函数间的传送 includeintstrlen chars voidmain char str cprogram intlen len strlen str 字符串首指针为实参 printf Thefirststring slength d n len len strlen fortranlanguige 移串常量为实参 printf Thesecondstring slength d n len 8 6字符指针 续 intstrlen chars 自定义求字符串长度函数strlen for intn 0 s 0 s n returnn 运行结果 Thefirststring slength 9Thesecondstring slength 16例8 15中两次调用函数strlen 第一次以字符串首指针为实参 第二次以串常量为实参 8 7指针数组 因为指针是变量 因此用指向同一数据类型的指针来构成一个数组 就形成了指针数组 数组中的每个元素都是指针变量 根据数组的声明 指针数组中每个元素都为指向同一数据类型的指针 指针数组的声明格式为 类型标识 数组名 整型常量表达式 例如 int a 10 声明了一个指针数组a 数组中的每个元素都是指向整型量的指针 该数组由10个元素组成 即a 0 a 1 a 2 a 9 它们均为指针变量 a为该指针数组名 和数组一样 a是常量 不能对它进行增量运算 a为指针数组元素a 0 的地址 a i即为a i 的地址 a就是a 0 a i 就是a i 8 7指针数组 续 指针数组处理字符串更方便 更灵活 使用二维数组对处理长度不等的正文效率是很低的 而指针数组由于其中每个元素都是指针变量 因此通过地址运算来操作正文是十分方便的 例8 16用指针数组处理二维数组数据 includevoidmain intiArray 3 4 int iPoint 3 给二维数组iArray赋初值 for inti 0 i 3 i for intj 0 j 4 j iArray i j i 1 j 1 让指针数组iPoint分别指向3个一维数组 iPoint 0 iArray 0 iPoint 1 iArray 1 iPoint 2 iArray 2 按行输出二维数组元素 8 7指针数组 续 for i 0 i 3 i for intj 0 j 4 j printf iArray d d d i j iPoint i j printf n 运行结果 iArray 0 0 1iArray 0 1 2iArray 0 2 3iArray 0 3 4iArray 1 0 2iArray 1 1 4iArray 1 2 6iArray 1 3 8iArray 2 0 3iArray 2 1 6iArray 2 2 9iArray 2 3 12Pressanykeytocontinue 指针数组和一般数组一样 允许指针数组在声明时初始化 但由于指针数组的每个元素都是指针变量 它只能存放地址 所以对指向字符串的指针数组在说明赋初值时 是把存放字符串的首地址赋给指针数组的对应元素 8 7指针数组 续 例8 17把5个国名按字母顺序排列输出 include includevoidsort char name intn voidprint char name intn staticchar name CHINA AMERICA AUSTRALIA FRANCE GERMAN voidmain intiCount 5 print name iCount sort name iCount print name iCount 8 7指针数组 续 voidsort char name intn char pt inti j k for i 0 i0 k j if k i pt name i name i name k name k pt 8 7指针数组 续 voidprint char name intn inti for i 0 i n i printf s t name i printf n 运行结果 CHINAAMERICAAUSTRALIAFRANCEGERMANAMERICAAUSTRALIACHINAFRANCEGERMAN 8 8指针函数 所谓函数类型是指函数返回值的类型 在C语言中允许一个函数的返回值是一个指针 即地址 这种返回指针值的函数称为指针型函数 格式 类型说明符 函数名 参数 当然了 由于返回的是一个地址 所以类型说明符一般都是int 例如 int GetDate int aaa int int 函数返回的是一个地址值 经常使用在返回数组的某一元素地址上 8 8指针函数 续 例8 18指针函数应用 includeint GetDate intweek intday voidmain intweek day do printf Enterweek 1 5 day 1 7 n scanf d d 8 8指针函数 续 int GetDate intweek intday staticintWeekAndDay 5 7 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 1 1 return 运行结果 Enterweek 1 5 day 1 7 2 5120 0 8 9函数指针 在 语言中 一个函数总是占用一段连续的内存区 而函数名就是该函数所占内存区的首地址 我们可以把函数的这个首地址 或称入口地址 赋给一个指针变量 使该指针变量指向该函数 然后通过指针变量就可以找到并调用这个函数 我们把这种指向函数的指针变量称为 函数指针变量 声明格式如下 类型说明符 函数名 参数 其实这里不能称为函数名 应该叫做指针的变量名 这个特殊的指针指向一个返回整型值的函数 指针的声明类型和它指向函数的声明类型保持一致 指针名和指针运算符外面的括号改变了默认的运算符优先级 如果没有圆括号 就变成了一个返回整型指针的函数的原型声明 例如 void fptr 把函数的地址赋值给函数指针 可以采用下面两种形式 fptr 8 9函数指针 续 取地址运算符 8 9函数指针 续 voidFileFunc voidEditFunc intiValue voidmain intiValue 0 funcp FileFunc funcp funcp1 EditFunc funcp1 iValue 8 9函数指针 续 voidFileFunc printf FileFunc n voidEditFunc intiValue printf EditFunc n printf d n iValue 运行结果 FileFuncEditFunc0 8 10多级指针 如果一个指针变量存放的又是另一个指针变量的地址 则称这个指针变量为指向指针的指针变量 例如 char cp 指针的指针需要用到指针的地址 通过指针的指针 不仅可以访问它指向的指针 还可以访问它指向的指针所指向的数据 下面就是几个这样的例子 charc A char p 利用指针的指针可以允许被调用函数修改局部指针变量和处理指针数组 8 10多级指针 续 例8 20多级指针的应用 includevoidFindCredit int Point voidmain intvals 7 6 5 4 3 2 1 0 int fp vals FindCredit 8 10多级指针 续 voidFindCredit int Point while Point 0 if Point 0 break else Point 运算结果 4 8 10多级指针 续 现在让我们来对例8 20进行分析 首先用一个数组的地址初始化指针fp 然后把该指针的地址作为实参传递给函数FindCredit FindCredit 函数通过表达式 Point间接地得到数组中的数据 为遍历数组以找到一个负值 FindCredit 函数进行自增运算的对象是调用者的指向数组的指针 而不是它自己的指向调用者指针的指针 语句 Point 就是对形参指针指向的指针进行自增运算的 8 10多级指针 续 例8 21输出指针数组指向的各个字符串 includevoidmain char chName ptr WINDOWS MS DOS UNIX LINUX char ptr for inti 0 i 4 i ptr chName ptr i printf s n ptr 8 10多级指针 续 运行结果 WINDOWSMS DOSUNIXLINUX注意 一级指针 直接存放数据变量的地址的指针 实现的是间接访问 又称 单级间接寻址 简称 单级间址 二级指针则是间接的间接的访问 称为 二级间址 从理论上讲 间址方法可延伸到更多级 但三级及以上的指针极少用于程序 因为级数越多 越难理解 也容易出错 8 11void指针类型 void的字面意思是 无类型 void 则为 无类型指针 void 可以指向任何类型的数据 8 11 1void的含义 void真正的意义在于 1 对函数返回的限定 2 对函数参数的限定 众所周知 如果指针p1和p2的类型相同 那么我们可以直接在p1和p2间互相赋值 如果p1和p2指向不同的数据类型 则必须使用强制类型转换运算符把赋值运算符右边的指针类型转换为左边指针的类型 例如 float p1 int p2 p1 p2 8 11 1void的含义 续 其中p1 p2语句会编译出错 必须改为 p1 float p2 而void 则不同 任何类型的指针都可以直接赋值给它 无需进行强制类型转换 void p1 int p2 p1 p2 但这并不意味着 void 也可以无需强制类型转换地赋给其它类型的指针 因为 无类型 可以包容 有类型 而 有类型 则不能包容 无类型 8 11 2void使用规则 下面给出void关键字的使用规则 如果函数没有返回值 那么应声明为void类型在C语言中 凡不加返回值类型限定的函数 就会被编译器作为返回整型值处理 但是许多程序员却误以为其为void类型 例8 22void类型示范 includeadd inta intb returna b voidmain printf 2 3 d n add 2 3 8 11 2void使用规则 续 运行结果 2 3 5这说明不加返回值说明的函数的确为int函数 另外 加上void类型声明后 也可以发挥代码的 自注释 作用 代码的 自注释 即代码能自己注释自己 如果函数无参数 那么应声明其参数为void在C语言中声明一个这样的函数 intfunction void return1 则进行下面的调用是不合法的 function 2 8 11 2void使用规则 续 因为在C中 函数参数为void的意思是这个函数不接受任何参数 所以 在C语言中 若函数不接受任何参数 要指明参数为void 小心使用void指针类型按照ANSI AmericanNationalStandardsInstitute 标准 不能对void指针进行算法操作 即下列操作都是不合法的 void pvoid pvoid ANSI 错误 pvoid 1 ANSI 错误 ANSI标准之所以这样认定 是因为它坚持 进行算法操作的指针必须是确定知道其指向数据类型大小的 例如 int pint pint ANSI 正确 pint 的结果是使其增大sizeof int 但是大名鼎鼎的GNU GNU sNotUnix的缩写 则不这么认定 它指定void 的算法操作与char 一致 8 11 2void使用规则 续 因此下列语句在GNU编译器中皆正确 pvoid GNU 正确 pvoid 1 GNU 正确 pvoid 的执行结果是其增大了1 在实际的程序设计中 为迎合ANSI标准 并提高程序的可移植性 我们可以这样编写实现同样功能的代码 void pvoid char pvoid ANSI 正确 GNU 正确 char pvoid 1 ANSI 错误 GNU 正确 GNU和ANSI还有一些区别 总体而言 GNU较ANSI更 开放 提供了对更多语法的支持 但是我们在真实设计时 还是应该尽可能地迎合ANSI标准 如果函数的参数可以是任意类型指针 那么应声明其参数为void 8 11 2void使用规则 续 典型的如内存操作函数memcpy和memset的函数原型分别为 void memcpy void dest constvoid src size tlen void memset void buffer intc size tnum 这样 任何类型的指针都可以传入memcpy和memset中 这也真实地体现了内存操作函数的意义 因为它操作的对象仅仅是一片内存 而不论这片内存是什么类型 下面的代码执行正确 例如 memset接受任意类型指针intintarray 100 memset intarray 0 100 sizeof int 将intarray清0 例如 memcpy接受任意类型指针intintarray1 100 intarray2 100 memcpy intarray1 intarray2 100 sizeof int 将intarray2拷贝给intarray1 8 11 2void使用规则 续 有趣的是 memcpy和memset函数返回的也是void 类型 void不能代表一个真实的变量下面代码都企图让void代表一个真实的变量 因此都是错误的代码 voida 错误 function voida 错误 void的出现只是为了一种抽象的需要 例8 23void指针类型应用实例 include include 8 11 2void使用规则 续 voidmain void ptr NULL charchName 10 Hello char chptr chptr chName printf chptr chName n printf chName s tchptr s n chName chptr printf c
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年中国甜八宝数据监测报告
- 快递考试题目及答案
- 空中乘务考试题及答案
- 果蔬坚果加工工岗位操作技能考核试卷及答案
- 炼钢准备工专项考核试卷及答案
- 2025年中国多功能平板跑步机数据监测研究报告
- 矿用发电车操作工基础考核试卷及答案
- 综合布线装维员上岗考核试卷及答案
- 精神控制考试题及答案
- 金蝶软件考试题及答案
- 桩基础平法施工图(平法施工图识读)
- GB/T 9113-2010整体钢制管法兰
- GB/T 23338-2018内燃机增压空气冷却器技术条件
- 癫痫的急救与护理课件
- 海姆立克急救法完整版本课件
- 国家地表水环境质量监测网采测分离实施方案课件
- 控压钻井技术及实践培训讲义工艺课件
- 厚度仪点检表
- 北京市水利工程维修养护定额
- 自然拼读法在小学英语教学中的应用的实践研究
- 无领导小组面试评分表模板
评论
0/150
提交评论