版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、第第8章章 指指 针针 指针指针是是C 语言中的一个重语言中的一个重要概念,是要概念,是C 语言的语言的 精华、灵精华、灵魂魂。1. 内存单元地址内存单元地址程序中定义了变量后,在程序中定义了变量后,在编译时编译时系统就给这个变量分配内存单元。系统就给这个变量分配内存单元。内存中每一个内存单元都有一个编号,这就是内存中每一个内存单元都有一个编号,这就是“地址地址”。内存单元地址内存单元地址 就是编译后系统分配给变量的就是编译后系统分配给变量的内存空间位置内存空间位置。例如:例如: int i , j, k; 8.1 地址和指针的概念地址和指针的概念内存用户数据区内存用户数据区变量k变量j变量i
2、200020022004其中其中2000为为 i 的地址的地址 2002为为 j 的地址的地址 2004为为 k 的地址的地址 数据在内存中的存取方式:数据在内存中的存取方式:直接访问方式直接访问方式 和和 间接访问方式。间接访问方式。 直接访问方式:直接访问方式:按变量地址存取变量值。按变量地址存取变量值。间接访问方式:间接访问方式: 把一个变量的地址放在另一个变量中把一个变量的地址放在另一个变量中。 内存单元的内容内存单元的内容 内存单元内存单元中存放的中存放的数值数值。 例如:例如:i=3 ;j=6 ; 在在2000单元中存放数值单元中存放数值 3,3 即为即为内容内容。 在在2002单
3、元中存放数值单元中存放数值 6,6 即为即为内容内容。变量 i变量 j 6320002002例:例: printf(%d, i ) ;其执行是这样的:其执行是这样的:根据变量名与地址的对应关系根据变量名与地址的对应关系,找到变,找到变量量i 的地址的地址2000,然后从由,然后从由2000开始的两个字节中取出数据(即变开始的两个字节中取出数据(即变量的值量的值3),把它输出。),把它输出。这种按变量地址存取变量值的方式称为这种按变量地址存取变量值的方式称为“直接访问直接访问”方式。方式。例如,我们定义一个变量例如,我们定义一个变量 i_pointer ,用来存放整型变量,用来存放整型变量i 的
4、地址:的地址: i_pointer = &i ; /* 把变量把变量 i 的地址的地址赋给变量赋给变量 i_pointer */这时这时i_pointer的值的值 就是变量就是变量 i 所占用的内存单元的所占用的内存单元的起始地址起始地址(2000)。)。如何存取变量如何存取变量i 的值?的值?变量 i变量 j 20006320002002变量变量i_pointer3010间接访问方式间接访问方式: 通过存储在某一个内存单元中的地址去存取该地址所通过存储在某一个内存单元中的地址去存取该地址所指向的内存单元中的变量值。指向的内存单元中的变量值。 要存取变量要存取变量i 的值,的值,先要找
5、到存放先要找到存放i 的地址的的地址的变量(变量(i_pointer),从中),从中取出取出i 的地址(的地址(2000),),然后到然后到2000、2001字节字节中取出中取出i 的值(的值(3)。)。表示将数值表示将数值3送到变量中,有两种方法:送到变量中,有两种方法:将将3 送到变量送到变量i 所标志的单元中。所标志的单元中。将将3 送到变量送到变量i_pointer 所所“指向指向”的单元中的单元中。 所谓所谓“指向指向”就是通过地址来体现的就是通过地址来体现的。(如右图所示)(如右图所示) 由于通过地址可以找到所需的变量单元,因此可以说,由于通过地址可以找到所需的变量单元,因此可以说
6、,地址地址“指向指向”该该内存单元内存单元。在。在C 语言中,语言中,将地址形象化地称为将地址形象化地称为“指针指针”。 一个变量的地址称为该变量的一个变量的地址称为该变量的“指针指针”。例如地址。例如地址2000是变量是变量i的指针的指针。 如果有一个变量,专门用来存放另一个变量的地址(即指针),则它称如果有一个变量,专门用来存放另一个变量的地址(即指针),则它称为为“指针变量指针变量 ”。如前面所说的。如前面所说的i_pointer变量称为变量称为指针变量。指针变量。指针变量的值指针变量的值是指针(地址)。是指针(地址)。注意:注意: (1) “指针指针” 和和 “指针变量指针变量” 这两
7、个不同的概念。这两个不同的概念。(2) 通过指针变量如何访问变量通过指针变量如何访问变量 i 的值?的值? (间接访问间接访问)2000i32000i32000i_pointer 变量的指针变量的指针就是变量的地址。存放变量地址的变量是就是变量的地址。存放变量地址的变量是指针变量,指针变量,用来指用来指向另一个变量。向另一个变量。 指针变量和它所指向的变量之间,用指针变量和它所指向的变量之间,用“ * ” 表示表示“ 指向指向”。例如例如 i_pointer代表指针变量,代表指针变量,* i_pointer 是是i_pointer所指向的变量。所指向的变量。 8.2 变量的指针和指向变量的指针
8、变量变量的指针和指向变量的指针变量* i_pointer200032000i_pointeri可以看到,可以看到,* i_pointer也代表一个变也代表一个变量,它和变量量,它和变量i 是同一回事。下面两是同一回事。下面两个语句作用相同:个语句作用相同: i=3; * i_pointer=3;第个语句的含义是将第个语句的含义是将3 赋给指针变赋给指针变量量i_pointer所指向的变量。所指向的变量。8.2.1 定义一个指针变量定义一个指针变量指针变量是专门用来存放地址的,因此必须定义为指针变量是专门用来存放地址的,因此必须定义为“ 指针类型指针类型”。指针变量定义的一般形式指针变量定义的一
9、般形式: 基类型基类型 * 指针变量名指针变量名指针变量的基类型指针变量的基类型 用来指定该指针变量可以指向的变量的类型。用来指定该指针变量可以指向的变量的类型。 例如例如: int *pointer_1,*pointer_2 ;(指向整型变量的指针变量)float *pointer_3;(指向实型变量的指针变量)char *pointer_4;(指向字符型变量的指针变量)注意注意: 1. 指针变量前面的指针变量前面的“ * ” 表示该变量是指针变量。表示该变量是指针变量。 指针变量名指针变量名是是 pointer_1、pointer_2 ,而不是,而不是*pointer_1、*pointer
10、_2 .2. 在定义指针变量时必须指定基类型。在定义指针变量时必须指定基类型。 不同类型的数据在内存中所占的字节数是不同的。不同类型的数据在内存中所占的字节数是不同的。指针变量的类型说明指针变量的类型说明 是为了告诉系统按变量中的地址从内存选取是为了告诉系统按变量中的地址从内存选取几个字节几个字节的数据进行操作,便于的数据进行操作,便于 指针的移动和指针的运算操作。指针的移动和指针的运算操作。3. 一个指针变量只能一个指针变量只能 指向同一类型的变量,即存放同一类型变量的地址。指向同一类型的变量,即存放同一类型变量的地址。怎样使一个指针变量指向另一个变量呢?怎样使一个指针变量指向另一个变量呢?
11、 可以使用赋值语句。可以使用赋值语句。例如:例如: float x ;char ch ;float * pointer_3 ;char * pointer_4 ;pointer_3 = &x ;pointer_4 = &ch ;例:例:int k, i=5;int *pointer;pointer=&i ;k = *pointer ;printf(%d, k);printf(%d , *pointer) ; 2000(地址地址)pointer20005(数据数据)*pointeri2000输出变量输出变量 i 的值的值5k的值为58.2.2 指针变量的引用指针变量的引用
12、指针变量中只能存放地址(指针)指针变量中只能存放地址(指针),不能将一个整型量或任何其它,不能将一个整型量或任何其它非地址类型的数据赋给一个指针变量。非地址类型的数据赋给一个指针变量。例如赋值语句:例如赋值语句:pointer_1=2000; (不合法)(不合法)有关地址的运算符有关地址的运算符: 1. & 取地址运算符。取地址运算符。2. * 指针运算符(或称指针运算符(或称“间接访问间接访问”运算符)。运算符)。例如例如: &a - 表示变量表示变量a的地址的地址 *p - 表示指针变量表示指针变量p 所指向的变量所指向的变量 ( 地址地址p所指的单元中存放的值所指的单元中
13、存放的值 )main( ) int a , b; int *pointer_1 ,*pointer_2; /*定义指针变量定义指针变量,指向整型变量指向整型变量*/ a=100; b=10; pointer_1=&a ; /* 使使pionter_1指向指向a */ pointer_2=&b ; /* 使使pionter_2指向指向b */ printf(%d,%dn, a , b) ; printf(%d,%dn, *pointer_1 , *pointer_2) ;例例8.1 通过指针变量访问整型变量。通过指针变量访问整型变量。运行结果:运行结果:100,10100,10&
14、amp;a(地址地址)pointer_1100(数据数据)*pointer_1a&b(地址地址)pointer_210(数据数据)*pointer_2bpointer_1 指向指向a,*pointer_1 就是变量就是变量a 。pointer_2 指向指向b, *pointer_2就是变量就是变量b 。& 和和 * 的优先级相同,按的优先级相同,按自右向左自右向左方向结合。因此,方向结合。因此,先执行先执行*pointer_1 的运算,的运算,它的执行结果就是变量它的执行结果就是变量 a ,再执行再执行& 运算,则运算,则&*pointer_1的最后结果与的最后
15、结果与 &a 相同,相同, 即取变量即取变量 a 的地的地址。址。(见图示1) &a &bpointer_2b apointer_1图示1*pointer_1*pointer_2a &a &apointer_2bpointer_1图示2*pointer_2*pointer_1下面对下面对 “&” 和和 “ * ” 运算符再做些说明运算符再做些说明: 如有如有 pointer_1=&a ; pointer_2=&b ; 则则 表达式表达式 &*pointer_1 的含义是什么?的含义是什么?pointer_2=&a;
16、 如果有如果有 pointer_2=&*pointer_1; 则结果如何?则结果如何? 先执行先执行 &a 运算,得运算,得 a 的地址,再执行的地址,再执行 * 运算,运算,得变量得变量 a 的值。的值。所以所以 , 表达式表达式 *&a 和表达式和表达式 *pointer_1 的含义是相同的,它们等价于变量的含义是相同的,它们等价于变量a。 即即*&a 与与a 等价。等价。 (3) (*pointer_1)+ 相当于相当于 a+, 变量变量a 的值加的值加1。(4) *pointer_1+ 相当于相当于 *(pointer_1+)。这是由于这是由于* 和和
17、+ + 优先级别相同,结合方向是优先级别相同,结合方向是自右而左自右而左。+ 在在pointer_1的右侧,是的右侧,是“后加后加”,因此,因此先执行先执行 *pointer_1,取取出出 a 的值的值,然后,然后指针指针 pointer_1加加1,这时,这时pointer_1就不再指向就不再指向a 了。了。注意:注意: (3) 和和(4) 的运算结果是不同的。的运算结果是不同的。apointer_1 &a5* pointer_1 * &a(2) 如有如有 pointer_1=&a ; 则表达式则表达式 *&a 的含义是什么?的含义是什么?main() int
18、a , b ,*p1 , *p2 , *p; scanf(%d,%d, &a , &b); p1=&a; p2=&b; if(ab) p=p1; p1=p2; p2=p; printf(a=%d,b=%dn, a , b); printf(max=%d,min=%dn,*p1 ,*p2);例例8.2 输入整数输入整数a和和b,按先大后小的顺序输出,按先大后小的顺序输出a和和b 。 &a &b p1p2 5 9 ab p &b &a p1p2 5 9 ab p&a运行结果运行结果: a=5 , b=9 max=9 , min
19、=5实际上并没有交换实际上并没有交换a 和和b。算法不交换整型变量。算法不交换整型变量的值(变量的值(变量a和和 b中的值没有改变),而是交换中的值没有改变),而是交换两个指针变量的值(即变量两个指针变量的值(即变量a和和 b的地址),使的地址),使p1指向大的数,指向大的数,p2指向小的数。指向小的数。8.2.3 指针变量作为函数参数指针变量作为函数参数例:对输入的两个整数按大小顺序输出。例:对输入的两个整数按大小顺序输出。 #include void main()void swap(int *,int *);int ,;int *pointer_,*pointer_; scanf(,);p
20、ointer_ ; pointer_2 ; if()swap( pointer_ , pointer_2 );printf(,); void swap(int *,int *) int temp; temp*1; *; *temp; 例:对输入的三个整数按大小顺序输出。例:对输入的三个整数按大小顺序输出。 #includevoid main() int a,b,c,*p1,*p2,*p3; scanf(%d,%d,%d,&a,&b,&c); p1=&a;p2=&b; p3=&c; exchange(p1,p2,p3); printf(n%d,%d
21、,%d n,a,b,c); void exchange(int *q1,int *q2,int *q3) if(*q1*q2)s);if(*q1*q3)s);if(*q2*q3)s);void s *pt1,int *pt2)int temp; temp=*pt1; *pt1=*pt2; *pt2=temp;一个变量有地址,一个数组包含若干元素,每个元素在内存中占用一个变量有地址,一个数组包含若干元素,每个元素在内存中占用存储单元,它们也应该有相应的地址。一个变量既然可以指向变量,当然存储单元,它们也应该有相应的地址。一个变量既然可以指向变量,当然也可以指向数组和数组元素。也可以指向数组和数组
22、元素。所谓所谓数组的指针数组的指针是指是指 数组的数组的起始地址。起始地址。数组元素的指针数组元素的指针是是数组元素的地址数组元素的地址 。例如:例如:int a10 , i , *p, *p1;p= a; 或或 p=&a0 ;p1=&a6 ;a0a1a6. . . . .&a0&a1&a6 app1=&a6p18.3 数组的指针和指向数组的指针变量数组的指针和指向数组的指针变量 引用数组元素可以用下标法(如引用数组元素可以用下标法(如a3),也可以用指针法,即通过指向数组元也可以用指针法,即通过指向数组元素的指针找到所需的元素。使用指针法素的指
23、针找到所需的元素。使用指针法能使目标程序质量高(占用内存少,运能使目标程序质量高(占用内存少,运行速度快)。行速度快)。8.3.1 指向数组元素的指针指向数组元素的指针例如例如: int a10; int *p; . p=&a0; 指向数组元素的指针变量,其定义与普通指针变量的定义相同。指向数组元素的指针变量,其定义与普通指针变量的定义相同。把元素把元素a0 的地址赋给的地址赋给 p, 即即p指向指向 数组数组a 的第的第0个元个元素。素。 因为数组名表示数组的首地址,所以它因为数组名表示数组的首地址,所以它 等价于等价于 p=a ;应注意数组应注意数组a 并不代表整个数组并不代表整个
24、数组,而是把数组,而是把数组a 的首地址的首地址赋给指针变量赋给指针变量p,不是把数组,不是把数组a 的各个元素赋给的各个元素赋给p。 指针变量定义的同时指针变量定义的同时 可以可以赋初值赋初值:int a10;int *p=&a0; 定义时也可以写成如下形式:定义时也可以写成如下形式:int *p=a;相当于相当于 int *p; p=&a0;a 数组数组a0a1a2aip*(p+i)a10&a0假设已经定义了数组和一个指针变量假设已经定义了数组和一个指针变量 int a10; int *p=a; . *p=1;数组元素的引用可以用数组元素的引用可以用 1. 下标法下
25、标法: ai2. 指针法指针法: *(a+i) 或或 *(p+i) 其中其中p 为指向数组为指向数组a 的指针的指针变量,初值变量,初值 p=a;下标法比较直观下标法比较直观,程序容易阅读程序容易阅读指针表示指针表示8.3.2 通过指针引用数组元素通过指针引用数组元素对对p 当前所指的当前所指的元素元素 赋整数值赋整数值1注意注意: C规定规定 :p+1 (或或p+) 指向同一数组的下指向同一数组的下一个元素一个元素 (不是地址值简单加不是地址值简单加1)。p+1代代表的实际地址是表的实际地址是 p+1d(d是一个数组是一个数组元素所占的字节数)。元素所占的字节数)。当当 p 的初值为的初值为
26、 a 的首地址时的首地址时, p+i 或或 a+i 就是就是ai 的地址,因为的地址,因为 a 代表了数组的代表了数组的首地址。首地址。*(p+i) 或或*(a+i) 所指的数组元素就是所指的数组元素就是ai 的内容。的内容。 实际上,实际上, 是变址运算符,是变址运算符,对对ai 的处理,是将的处理,是将ai 按按a+i 计算地址计算地址,然后找出此地址单元中的值。,然后找出此地址单元中的值。1.指向数组的指针变量也可以带下标指向数组的指针变量也可以带下标, 如如: pi 等价于等价于*(p+i) ,即,即ai 。a 数组数组a0a1a2aip+1 或或 a+1pp+2 或或 a+2p+i
27、或或 a+i*(p+i)p+9 或或 a+9a9例例8.5 输出数组中全部元素输出数组中全部元素 :四种方法。种方法。 main( ) int a10=1,2,3,4,5,6,7,8,9,10; int *p, i ; printf(n下标法下标法:n) ; for(i=0 ; i10 ; i+) printf(%d, ai) ; printf(n指针法指针法(数组名数组名):n); for(i=0 ; i10 ; i+) printf(%d, *(a+i) ; printf(n指针法指针法(指针变量指针变量):n) ; p=a; for(i=0 ; i10 ; i+) printf(%d,
28、*(p+i) ; printf(n指针法指针法(指针变量指针变量):n) ; for(p=a ; pa+10 ; p+) printf(%d ,*p) ; printf(n) ; 否!否!a+的操作是非法的,因的操作是非法的,因 a是常量是常量。能否改成如下形式能否改成如下形式? for(p=a;ap+10; a+) printf(%d,*a)几种方法的比较:几种方法的比较:下标法和指针法(数下标法和指针法(数组名)执行效果是相组名)执行效果是相同的。编译系统先将同的。编译系统先将ai 转换为转换为*(a+i),即,即先计算元素地址,因先计算元素地址,因此这两种方法找数组此这两种方法找数组元素
29、比较费时。元素比较费时。指针变量法是直接指指针变量法是直接指向数组元素,向数组元素,p+的自的自加操作比较快加操作比较快。1)用下标法比较直观用下标法比较直观。例例 8.6 通过指针变量输入输出数组通过指针变量输入输出数组 a 的的10个元素个元素 main( ) int *p , i , a10 ;p=a ;for(i=0 ; i10 ; i+ ) scanf(%d, &ai ) ;printf(n) ; p=a ;for(i=0; i10 ; i+ ) printf(%d, * p+) ; 如果输出时没有语句如果输出时没有语句 p=a; 程序将程序将 ? P是地址,是地址,不要加不
30、要加 &出错,因输入时已改变了指针出错,因输入时已改变了指针 p 的指向。的指向。 p+a 数组数组a0a1aip+pa9(1) p+(或或 p+=1),使,使 p指向下一个元素,即指向下一个元素,即 a1。(2) *p+, 相当于相当于 *(p+),先执行,先执行 * p,后执行,后执行 p+。(3) *p+ 和和 *(+p) 作用不同。作用不同。 *p+ 是取出是取出 a0 的值,后的值,后 p+; *(+p) 是先使是先使p 加加1,再取,再取*p,即,即 取出取出 a1 的值。的值。(4) (*p)+, 表示表示 p 所指向的元素值加所指向的元素值加 1,即,即 (a0)+,如
31、若,如若 a0=3,则,则 (a0)+的值为的值为 4。(5) 如果如果 p 指向指向 a 数组中第数组中第 i 个元素,则:个元素,则: *(p- -) 相当于相当于 ai- -,先对,先对 p进行进行 “ * ”运算,再使运算,再使 p- -。 *(+p) 相当于相当于 a+i,先使,先使 p+,再作,再作 “ * ” 运算。运算。 *(- -p) 相当于相当于 a- -i,先使,先使 p- -,再作,再作 “ * ” 运算。运算。注意:指针变量的运算。若注意:指针变量的运算。若 p=a ; 则则:一、一、 多维数组的地址多维数组的地址 例例. 二维数组二维数组 int a34=1,3,5
32、,7,9,11,13,15,17,19,21,23; 分析二维数组的性质:分析二维数组的性质: a 是一个数组名,包含是一个数组名,包含3个元素:个元素: a0,a1,a2。 每个元素又是一个一维数组,包含每个元素又是一个一维数组,包含4个元素。个元素。a0a1a2a 数组数组aa+1a+2(2008)(2000)(2016)8.3.4 指向多维数组的指针和指针变量指向多维数组的指针和指针变量a0a1 a2a2321191715131197531 从二维数组的角度看,从二维数组的角度看,a 代表代表整个二维数组的整个二维数组的首地址首地址,也就是,也就是第第0行的首地址行的首地址。若若a 的首
33、地址为的首地址为2000,因为每一行,因为每一行4个元素,占用个元素,占用8个字节,所以则个字节,所以则 a+1的首地址为的首地址为2008,a+2的首地的首地址为址为2016。故故a+1 代表第代表第1行的首地址,即行的首地址,即a1的地址;的地址;a+2 代代表第表第2行的首行的首地址,即地址,即a2的地址的地址。 由于由于a0 与与 *(a+0) 等价,等价,a1 与与 *(a+1) 等价,等价,ai 与与 *(a+i) 等价等价,因,因此此ai+j 与与*(a+i)+j 等价,它们都是元素等价,它们都是元素aij 的地址的地址。故:元素故:元素aij 的地址的地址表示是表示是: ai+
34、j 或或 *(a+i)+j 而而 *(*(a+i)+j) 或或 *(ai+j) 是元素是元素 aij的值的值。2000 2002 2004 2006 1 3 5 72008 2010 2012 2014 9 11 13 152016 2018 2020 2022 17 19 21 23 a a0a1a2a0 a0+1 a0+2 a0+3 a0、a1、a2既然是一维数组名,则既然是一维数组名,则它们应代表一维数组的首地址。它们应代表一维数组的首地址。 a0代表代表第第0行行一维数组中一维数组中第第0列列元素元素的地址,即的地址,即&a00; a0+1代表代表第第0行第行第1列元素的地址,
35、即列元素的地址,即&a01 。a1代表代表第第1行行一维数组中一维数组中第第0列列元素元素的地址,即的地址,即&a10; 。a2代表代表第第2行行一维数组中一维数组中第第0列列元素元素的地址,即的地址,即&a20。表表 10.1表示形式表示形式含含 义义地地 址址二维数组名,数组首地址,二维数组名,数组首地址,0行首地址行首地址 2000 第第 0 行第行第 0 列元素地址列元素地址 2000第第 1 行首地址行首地址 2008第第 1 行第行第 0 列元素地址列元素地址 2008第第 1 行第行第 2 列元素地址列元素地址 2012 第第 1 行第行第 2 列元素的值
36、列元素的值 元素值为元素值为 13aa0+0 , *(a+0) , *aa+1 , &a10a1+0 , *(a+1)a1+2 , *(a+1)+2 , &a12*(a1+2),*(*(a+1)+2),a12aa+1a+22022232020212018192016172014152012132010112008920067200452002320001a0+3a0+2a0+1a0+0牢记牢记: 二维数组名(如二维数组名(如 a)是指向)是指向行行的的,因此因此a+1中的中的“1” 表示表示一行元素一行元素。 一维数一维数组名(如组名(如a0、a1)是指向)是指向列列的,的,a
37、0+1 中的中的 “1” 代表一个元素。代表一个元素。 在在行行指针前面加一个指针前面加一个*,就,就转换为列转换为列指指针。如针。如 a、a+1(行)(行) *a、*(a+1)(列)(列)在在列列指针前面加一个指针前面加一个&,就转换为行指针,就转换为行指针.如如 a0(列)(列) &a0(行,等价于(行,等价于&*a,即与即与a 等价)等价)例例 8.11 输出二维数组有关的值。输出二维数组有关的值。#define FORMAT %d,%dn”main( ) int a34 =1,3,5,7,9,11,13,15,17,19,21,23; printf(FORMAT
38、, a , *a) ;1 printf(FORMAT, a0 , *(a+0) ;2 printf(FORMAT, &a0 , &a00) ;3 printf(FORMAT, a1 , a+1) ;4 printf(FORMAT, &a10 , *(a+1)+0) ;5 printf(FORMAT, a2 , *(a+2) ;6 printf(FORMAT, &a2 , a+2) ;7 printf(FORMAT, a10 , *(*(a+1)+0) );8(假设系统给数组(假设系统给数组 a 分分配的首地址为配的首地址为 2000)则运行结果是:则运行结果是:
39、注意:注意:a 是二维数组名,代表数组首地址,不能企图用是二维数组名,代表数组首地址,不能企图用*a来得到来得到a00的值。的值。*a 相当于相当于*(a+0) ,即,即a0,它是第,它是第0行地址。行地址。a是行指针,是行指针,*a是列指针,指向是列指针,指向0行行0列元素。列元素。*a是是0行行0列元素的值。列元素的值。1.a+1指向第一行首地址,不能企图用指向第一行首地址,不能企图用*(a+1)得到得到a10的值,而应该用的值,而应该用*(a+1) 求求a10元素的值。元素的值。2000, 20002000, 20002000, 20002008, 20082008, 20082016,
40、 20162016, 20169, 9例例 8.12 用指针变量输出数组元素的值。用指针变量输出数组元素的值。main( ) int a34=1,3,5,7,9,11,13,15,17,19,21,23 ; int *p ; for(p=a0 ; pa0+12 ; p+) printf(%4d , *p) ; 运运行结果:行结果: 1 3 5 7 9 11 13 1517 19 21 23p-a0:为相距的单:为相距的单元个数,不是字节数元个数,不是字节数若:若: a0 2000 p 2008则:则: p-a0 = 4 1. 指向数组元素的指针变量指向数组元素的指针变量二、指向多维数组的指针变
41、量二、指向多维数组的指针变量2123191711131597531pa if(p-a0)%4 = = 0) printf(n) ;数组元素在数组中的位置数组元素在数组中的位置:计算某个指定的数组元素在数计算某个指定的数组元素在数组中的位置(即相对于数组起始位置组中的位置(即相对于数组起始位置的的相对位移量相对位移量):): 设设a是大小为是大小为 nm 的二维数组,则的二维数组,则 元素元素 aij 在在数组中的数组中的相对位置相对位置的计算公式为:的计算公式为: i*m+j 若若 p 是一个指针变量,指向二是一个指针变量,指向二维数组维数组 a 的首地址:的首地址: p=&a00 或
42、或 p=a0,则,则 元素元素 aij 的地址为的地址为 p+i*m+j,故故 aij 可以表示成:可以表示成:*(p+i*m+j) 。如如 a12位置是:位置是: 1*4+2如如 a12地址是:地址是: p+ 1*4+2则则 a12的的值是值是: *(p+1*4+2)a12mna,p以前的例子都是输出数组全部元素,若是输出某个指定的数组元以前的例子都是输出数组全部元素,若是输出某个指定的数组元素该怎么办呢?素该怎么办呢?应该事先计算出该元素在数组中的位置。应该事先计算出该元素在数组中的位置。2. 指向由指向由m个整数组成的一维数组的指针变量个整数组成的一维数组的指针变量数组指针数组指针由于由
43、于 * 运算符的级别低于运算符的级别低于 ,为了定义指,为了定义指针变量针变量 p,就必须表示成,就必须表示成 (*p) 的形式。的形式。定义一个定义一个数组指针数组指针变量变量 p , 指向包含指向包含 4 个元素的一维数组。个元素的一维数组。 定义方法:定义方法: int (*p)4;a0a1a2a 数组数组pp+1p+2*p 有有 4 个整型元素,即个整型元素,即 指针指针 p 指向含有指向含有 4 个整型元素的一维个整型元素的一维数组数组, p 的值就是该一维数组的首地址,的值就是该一维数组的首地址,p是数组指针。是数组指针。(*p)3(*p)2(*p)1(*p)0p*p(数组)(数组
44、)若定义:若定义:int a34;p=&a0; /*p指向指向 a0*/则则p+1 不是指向不是指向a01,而是,而是a1,p的增值以一维数组的长度为单位。的增值以一维数组的长度为单位。故故aij可表示为:可表示为:*(*(p+i)+j)。例例 8.13 输出二维数组任一行任一列元素的值。输出二维数组任一行任一列元素的值。main( ) int a34=1,3,5,7,9,11,13,15,17,19,21,23 ; int (*p)4 , i , j ; p=a ; scanf(%d,%d ,&i , &j) ; printf(a%d%d=%dn , i , j ,
45、*(*(p+i)+j) ;pp+21 3 5 79 11 13 1517 19 21 23运行结果:运行结果: 输入:输入: 1, 2 输出:输出: a12=13说明:说明:aij 的地址的地址可以表示为可以表示为: *(p+i) + j 。注意注意*(p+i) + j 不能写成不能写成 p+ i+j。对于对于数组指针数组指针p, p=a; 则则p 指向二维数指向二维数组组 a 的第一行首地址的第一行首地址 。1.对于对于指针变量指针变量 p,p=a0;则则 p 指向二维指向二维数组数组 a 的第一行第一列元素首地址,的第一行第一列元素首地址, 而而 p=a ; 是不合法是不合法 的。的。vo
46、id average(float *p, int n) float *p_end; float sum=0 , aver; p_end=p+n-1; for( ; p=p_end; p+ ) sum=sum + (*p); aver=sum/n; printf(average=%6.2n,aver);void search( float (*p)4 , int n) int j ; printf(the score of No. %d are:n, n); for (j=0 ; j4 ; j+) printf(%5.2f , *(*(p+n)+j) );由于由于 p 指向一维数组,因此,指向一
47、维数组,因此,*(p+n) 是第是第 n 行的首地行的首地址,址, *(p+n)+j 是是 第第 n 行第行第 j 列元素的地址。列元素的地址。 运行结果:运行结果: average=82.25 the score of No.2 are: 90.00 99.00 100.00 98.00最后一个元素的地址最后一个元素的地址从指向第一个元素起从指向第一个元素起,依次指向下一个元素依次指向下一个元素例例 8.15 在上例中,找出有一门以上不及袼的学生,输出其全部成绩。在上例中,找出有一门以上不及袼的学生,输出其全部成绩。void search( float (*p)4 , int n) int
48、i , j , flag; for(j=0 ; jn ; j+) flag=0 ; for (i=0 ; i4 ; i+) if(*(*(p+j)+i)60) flag=1 ; if(flag= = 1) printf(No. %d fails , his scores are :n , j+1); for(i=0 ; i4 ; i+) printf(%5.1f , *(*(p+j)+i) ); 程序运行结果:程序运行结果:No.1 fails,his scores are:65.0 57.0 70.0 60.0No.2 fails,his scores are :58.0 87.0 90.0
49、 81.08. 4. 1 字符串表示形式字符串表示形式在在C程序中,可以用两种方法访问一个字符串。程序中,可以用两种方法访问一个字符串。 1. 用字符数组存放一个字符串用字符数组存放一个字符串 例例 8.16 main( ) char string = I love China! ; printf(%sn , string) ;运行结果:运行结果:I love China!string I string0string13loveChina!08.4 字符串的指针和指向字符串的指针变量字符串的指针和指向字符串的指针变量字符串常量按字字符串常量按字符数组处理,在符数组处理,在内存中开辟了一内存中开
50、辟了一个字符数组来存个字符数组来存放字符串常量放字符串常量这里这里 string 是是数组名,代表数组名,代表这个字符数组这个字符数组的首地址。的首地址。 stringi就是就是*(string+i) 。2. 用用字符指针字符指针指向一个字符串指向一个字符串例例 8.17main( ) char *string = I love China! ; printf(%sn , string) ;注意:注意:不能认为不能认为string是一个字符串变量,以为是在定义时把是一个字符串变量,以为是在定义时把“I love China!” 赋给该字符串变量。赋给该字符串变量。 通过通过字符数组名或字符指针
51、变量字符数组名或字符指针变量可以输出一个字符串(可以输出一个字符串( %s 的格式),的格式),但是不能企图用数组名输出它的全部元素(但是不能企图用数组名输出它的全部元素(%d 只能输出一个元素值)。只能输出一个元素值)。由于可以用数组或指针来表示字符串,因此对字符串中字符的存取可以由于可以用数组或指针来表示字符串,因此对字符串中字符的存取可以用下标法,也可以用指针法。用下标法,也可以用指针法。 等价于等价于: char *string; string=I love China!;定义一个指向字符数据的指针,把定义一个指向字符数据的指针,把该该字符串的首地址字符串的首地址赋给赋给 string
52、定义一个指针变量定义一个指针变量 string ,指向字符串的首地址。指向字符串的首地址。例例 8.18 复制字符串复制字符串 a 到字到字符串符串 b。用。用下标方法下标方法: main( ) char a =I am a boy.; char b20; int i; for(i=0 ; *(a+i)!=0 ; i+) *(b+i)=*(a+i); *(b+i)=0; /* 复制复制0 */ printf(string a is :%sn,a); printf(string b is :); for(i=0 ; bi!=0 ; i+) printf(%c,bi); printf(n);mai
53、n( ) char a =I am a boy.; char b20; char *p1 ,*p2; int i ; for ( p1=a , p2=b ; *p1!=0 ; p1+ , p2+) *p2=*p1; *p2=0; /* 复制复制0 */ printf(a is :%sn, a) ; printf(b is :) ; /* 逐个输出逐个输出 b 中的元素中的元素 */ for(i=0 ; bi!=0 ; i+) printf(%c,bi); printf(n); 例例 8.19用用指针方法指针方法: 运行结果:运行结果:string a is :I am a boy.string
54、 b is :I am a boy.字符指针作函数参数字符指针作函数参数用函数调用实现字符串的复制用函数调用实现字符串的复制 (1) 用字符数组作参数用字符数组作参数#include void main() void copy_string(char from , char to ); char a = am a teacher; char =you are a student; printf(“string a= string ,); printf(“copy string a to string b:n ”); copy_string (,); printf(nstring a=%snst
55、ring b=%sn,a,b); void copy_string(char from , char to ) int ; while(from!)tofrom;to; (2) 形参用字符指针变量形参用字符指针变量 #include void main() void copy_string(char * from, char *); char * am a teacher .; char *you are a student ; printf(string a=string ,); printf(copy string a to string b:n ); _(,);printf(nstrin
56、g a=%snstring b=%sn,a,b); void copy_string(char *r,char *) for(;*from!;from,to)*tofrom;*to; (3) (3) 对对copy string 函数还可作简化函数还可作简化 将将copy_string函数改写为函数改写为void copy_string (char *from,char *) while(*from)!) to;from; copy_string函数的函数体还可改为函数的函数体还可改为 while(*to*from)!); copy_string函数的函数体还可写成函数的函数体还可写成 whil
57、e(*from!)*to*from; *to; 上面的上面的while语句还可以进一步简化为下面的语句还可以进一步简化为下面的while语语句:句:while(*to*from);它与下面语句等价:while(*to*from)!);将*from赋给*to,如果赋值后的*to值等于则循环终止(已赋给*to) 函数体中函数体中while语句也可以改用语句也可以改用for语句:语句:for(;(*to*from)!;);或for(;*to*from;);也可用指针变量,函数也可用指针变量,函数copy_string可写为可写为void copy_string (char from ,char )c
58、har*,*; from;while(*p2*p1)!);2. 初始化与赋值方式。初始化与赋值方式。 字符指针变量可以在定义时初始化,也可以用赋值语句赋值。字符指针变量可以在定义时初始化,也可以用赋值语句赋值。如,如,char *a =I love China! ; 与与 char *a ; a=I love China! ; 等价。等价。8.4.3 字符指针变量与字符数组的讨论字符指针变量与字符数组的讨论将字符串的将字符串的首地址首地址 赋给赋给该指针变量。该指针变量。两者都能用于字符串的存储和运算,但是两者是有区别的两者都能用于字符串的存储和运算,但是两者是有区别的:1.字符数组由若干元素
59、组成,每个元素中存放一个字符;字符指针变量字符数组由若干元素组成,每个元素中存放一个字符;字符指针变量中存放的是字符串的首地址,决不是将字符串放到字符指针变量中。中存放的是字符串的首地址,决不是将字符串放到字符指针变量中。不合法,二者不等价不合法,二者不等价 合法合法如,如,char str14=I love China! ;char str14 ; str =I love China! ; 对于字符数组,可以在定义时整体赋初值,但不能用赋值语句整体赋值。对于字符数组,可以在定义时整体赋初值,但不能用赋值语句整体赋值。3. 数组一经定义,就有了确定的内存单元。数组一经定义,就有了确定的内存单元
60、。 字符指针变量定义后,虽然也分配了内存单元,在没字符指针变量定义后,虽然也分配了内存单元,在没有对指针变量赋初值之前,这个单元中没有一个确定的地址有对指针变量赋初值之前,这个单元中没有一个确定的地址值,因此该指针并未具体指向某个字符数据。值,因此该指针并未具体指向某个字符数据。可以可以有时可以有时可以尽量不要用,因尽量不要用,因 a 没有确定的指向,没有确定的指向,程序运行时可能会破坏其它程序。程序运行时可能会破坏其它程序。 char str10; scanf(%s , str) ; char *a ; scanf(%s , a) ; 4. 数组名代表该数组的起始地址,它的值不能改变;指针变量的值可以改变数组名代表该数组的起始地址,它的值不能改变;指针变量的值可以改变 例例8.21main( ) char str =I love China! ; str=str+7 ; printf(%s , str) ; main( ) char *a=I love China! ; int j ; printf(The sixth character is %cn , a5) ; for(j=0 ; aj!= 0 ; j+) printf(%c , aj) ; 运行结果:运行结果:The sixth character is eI love Chi
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2024年家务助理临时派遣合同
- 2024年定制化全年铲车租赁服务合同
- 2024年国际跨境电商物流服务协议
- 2024年国际旅游岛开发与经营权转让合同
- 2024年保险合同保险责任及除外条款
- 2024-2025学年新教材高中生物第2章基因和染色体的关系第1节第2课时受精作用练习含解析新人教版必修第二册
- 湖南省衡阳市衡阳县2025届高三一模 数学试题含答案
- 2024年城市规划与建筑设计合同
- 2024年城市光纤宽带网络建设合同
- 网络平台技术服务协议
- 2024年采矿权转让合同范本
- 第六章 回归分析课件
- 期中阶段性练习(一~四单元)(试题)-2024-2025学年五年级上册数学苏教版
- 《病人安全护理学》-教学大纲
- 项目分成比例合同模板
- GB 27887-2024机动车儿童乘员用约束系统
- 朝鲜半岛局势演变
- 自然科学基金项目申请书
- 2024年云南德宏州州级事业单位选调工作人员历年高频难、易错点500题模拟试题附带答案详解
- 第四届全国新能源汽车关键技术技能大赛(新能源汽车轻量化技术方向)决赛参考试题库(含答案)
- 2024年秋新鲁科版三年级上册英语课件 Unit 6 lesson 1
评论
0/150
提交评论