版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
指针(zhǐzhēn)PPT讲座第一页,共74页。1.内存(nèicún)单元地址 程序中定义了变量后,在编译时系统就给这个变量分配内存(nèicún)单元。内存(nèicún)中每一个内存(nèicún)单元都有一个编号,这就是“地址”。 内存(nèicún)单元地址就是编译后系统分配给变量的内存(nèicún)空间位置。 例如:inti,j,k;8.1地址(dìzhǐ)和指针的概念内存用户数据区┇变量k变量j变量i┇200020022004其中2000为i的地址
2002为j的地址
2004为k的地址第二页,共74页。⑶数据在内存中的存取方式(fāngshì):直接访问方式(fāngshì)和间接访问方式(fāngshì)。 直接访问方式(fāngshì):按变量地址存取变量值。 间接访问方式(fāngshì):把一个变量的地址放在另一个变量中。⑵内存单元(dānyuán)的内容——内存单元(dānyuán)中存放的数值。例如:i=3; j=6;在2000单元(dānyuán)中存放数值3,3即为内容。在2002单元(dānyuán)中存放数值6,6即为内容。变量i变量j
┇63┇20002002例:printf("%d",i); 其执行是这样的:根据变量名与地址(dìzhǐ)的对应关系,找到变量i的地址(dìzhǐ)2000,然后从由2000开始的两个字节中取出数据(即变量的值3),把它输出。这种按变量地址(dìzhǐ)存取变量值的方式称为“直接访问”方式。第三页,共74页。例如,我们定义一个变量i_pointer,用来(yònɡlái)存放整型变量i的地址: i_pointer=&i; /*把变量i的地址赋给变量i_pointer*/这时i_pointer的值就是变量i所占用的内存单元的起始地址(2000)。如何存取变量i的值?变量i变量j
┇┇200063┇20002002变量i_pointer3010间接访问方式:通过存储在某一个内存(nèicún)单元中的地址去存取该地址所指向的内存(nèicún)单元中的变量值。要存取变量i的值,先要找到存放(cúnfàng)i的地址的变量(i_pointer),从中取出i的地址(2000),然后到2000、2001字节中取出i的值(3)。第四页,共74页。表示将数值(shùzí)3送到变量中,有两种方法:①将3送到变量i所标志的单元中。②将3送到变量i_pointer所“指向”的单元中。 所谓“指向”就是通过地址来体现的。 (如右图所示)由于通过地址可以找到所需的变量单元,因此可以说,地址“指向”该内存单元。在C语言中,将地址形象化地称为(chēnɡwéi)“指针”。一个变量的地址称为(chēnɡwéi)该变量的“指针”。例如地址2000是变量i的指针。如果有一个变量,专门用来存放另一个变量的地址(即指针),则它称为(chēnɡwéi)“指针变量”。如前面所说的i_pointer变量称为(chēnɡwéi)指针变量。指针变量的值是指针(地址)。注意: (1)“指针”和“指针变量”这两个不同的概念。 (2)通过指针变量如何访问变量i的值?(间接访问)2000i32000i32000i_pointer第五页,共74页。变量的指针就是变量的地址。存放变量地址的变量是指针变量,用来指向另一个变量。指针变量和它所指向的变量之间,用“*”表示“指向”。 例如(lìrú)i_pointer代表指针变量,*i_pointer是i_pointer所指向的变量。
8.2变量(biànliàng)的指针和指向变量(biànliàng)的指针变量(biànliàng)*i_pointer200032000i_pointeri可以看到,*i_pointer也代表一个变量,它和变量i是同一(tóngyī)回事。下面两个语句作用相同:①i=3;②*i_pointer=3;第②个语句的含义是将3赋给指针变量i_pointer所指向的变量。第六页,共74页。8.2.1定义(dìngyì)一个指针变量指针变量是专门用来存放地址的,因此必须定义为“指针类型”。指针变量定义的一般形式:基类型*指针变量名 指针变量的基类型用来指定该指针变量可以指向的变量的类型。例如(lìrú):int*pointer_1,*pointer_2; (指向整型变量的指针变量)float*pointer_3; (指向实型变量的指针变量)char*pointer_4; (指向字符型变量的指针变量)注意:1.指针变量前面的“*”表示该变量是指针变量。指针变量名是pointer_1、pointer_2,而不是*pointer_1、*pointer_2.2.在定义指针变量时必须指定基类型。 不同类型的数据在内存中所占的字节数是不同的。 指针变量的类型说明是为了告诉系统按变量中的地址(dìzhǐ)从内存选取几个字节的数据进行操作,便于指针的移动和指针的运算操作。3.一个指针变量只能指向同一类型的变量,即存放同一类型变量的地址(dìzhǐ)。第七页,共74页。怎样使一个指针变量指向另一个变量呢?可以(kěyǐ)使用赋值语句。例如:floatx; charch; float*pointer_3; char*pointer_4; pointer_3=&x; pointer_4=&ch;例:intk,i=5;int*pointer;pointer=&i;k=*pointer;printf("%d",k); printf("%d",*pointer);2000(地址)pointer20005(数据)*pointeri2000输出(shūchū)变量i的值5k的值为5第八页,共74页。8.2.2指针(zhǐzhēn)变量的引用 指针变量中只能存放地址(指针),不能将一个(yīɡè)整型量或任何其它非地址类型的数据赋给一个(yīɡè)指针变量。例如赋值语句:pointer_1=2000;(不合法)有关地址的运算符:1.&——取地址运算符。2.*——指针(zhǐzhēn)运算符(或称“间接访问”运算符)。例如:&a---表示变量a的地址*p---表示指针(zhǐzhēn)变量p所指向的变量(地址p所指的单元中存放的值)第九页,共74页。main(){inta,b;int*pointer_1,*pointer_2; /*定义(dìngyì)指针变量,指向整型变量*/a=100;b=10;pointer_1=&a; /*使pionter_1指向a*/pointer_2=&b; /*使pionter_2指向b*/printf("%d,%d\n",a,b);printf("%d,%d\n",*pointer_1,*pointer_2);}例8.1 通过(tōngguò)指针变量访问整型变量。运行(yùnxíng)结果:100,10100,10&a(地址)pointer_1100(数据)*pointer_1a&b(地址)pointer_210(数据)*pointer_2bpointer_1指向a,*pointer_1就是变量a。pointer_2指向b,*pointer_2就是变量b。第十页,共74页。&和*的优先级相同,按自右向左方向结合。因此,先执行*pointer_1的运算,它的执行结果就是(jiùshì)变量a,再执行&运算,则&*pointer_1的最后结果与&a相同,即取变量a的地址。(见图示1)&a&bpointer_2bapointer_1图示1*pointer_1*pointer_2a&a&apointer_2bpointer_1图示2*pointer_2*pointer_1下面对“&”和“*”运算符再做些说明:⑴如有pointer_1=&a; pointer_2=&b;则表达式&*pointer_1的含义(hányì)是什么?pointer_2=&a;如果(rúguǒ)有pointer_2=&*pointer_1;则结果如何?第十一页,共74页。先执行&a运算,得a的地址,再执行*运算,得变量a的值。所以,表达式*&a和表达式*pointer_1的含义是相同的,它们(tāmen)等价于变量a。即*&a与a等价。(3)(*pointer_1)++相当于a++,变量a的值加1。(4)*pointer_1++相当于*(pointer_1++)。 这是由于*和++优先级别相同,结合方向是自右而左。 ++在pointer_1的右侧,是“后加”,因此先执行(zhíxíng)*pointer_1,取出a的值,然后指针pointer_1加1,这时pointer_1就不再指向a了。注意:(3)和(4)的运算结果是不同的。apointer_1&a5*pointer_1*&a(2)如有pointer_1=&a; 则表达式*&a的含义(hányì)是什么?第十二页,共74页。main(){inta,b,*p1,*p2,*p;scanf("%d,%d",&a,&b);p1=&a;p2=&b;if(a<b){p=p1;p1=p2;p2=p;}printf("a=%d,b=%d\n",a,b);printf("max=%d,min=%d\n",*p1,*p2);}例8.2输入整数(zhěngshù)a和b,按先大后小的顺序输出a和b。
&a
&b
p1p2
5
9
ab
p
&b
&a
p1p2
5
9
ab
p&a运行(yùnxíng)结果:a=5,b=9max=9,min=5实际上并没有交换a和b。算法不交换整型变量的值(变量a和b中的值没有改变),而是交换两个指针变量的值(即变量a和b的地址(dìzhǐ)),使p1指向大的数,p2指向小的数。第十三页,共74页。8.2.3指针(zhǐzhēn)变量作为函数参数例:对输入的两个整数(zhěngshù)按大小顺序输出。
#include<stdio.h>voidmain(){voidswap(int*p1,int*p2);
inta,b;
int*pointer_1,*pointer_2;
scanf(″%d,%d″,&a,&b);
pointer_1=&a;pointer_2=&b;
if(a<b)
swap(pointer_1,pointer_2);
printf(″\n%d,%d\n″,a,b);
}
voidswap(int*p1,int*p2){
inttemp;
temp=*p1;*p1=*p2;*p2=temp;
}第十四页,共74页。第十五页,共74页。例:对输入的三个整数按大小(dàxiǎo)顺序输出。#include<stdio.h>voidmain(){inta,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,%d\n",a,b,c);}voidexchange(int*q1,int*q2,int*q3){if(*q1<*q2)swap(q1,q2);if(*q1<*q3)swap(q1,q3);if(*q2<*q3)swap(q2,q3);}voidswap(int*pt1,int*pt2){inttemp;temp=*pt1;*pt1=*pt2;*pt2=temp;}第十六页,共74页。 一个变量有地址,一个数组包含若干元素,每个元素在内存中占用(zhànyònɡ)存储单元,它们也应该有相应的地址。一个变量既然可以指向变量,当然也可以指向数组和数组元素。 所谓数组的指针是指数组的起始地址。数组元素的指针是数组元素的地址。 例如: inta[10],i,*p,*p1; p=a;或p=&a[0]; p1=&a[6];a[0]a[1]a[6]......&a[0]&a[1]&a[6]app1=&a[6]p18.3数组的指针和指向(zhǐxiànɡ)数组的指针变量引用数组元素可以用下标法(如a[3]),也可以用指针法,即通过指向数组元素的指针找到所需的元素。使用(shǐyòng)指针法能使目标程序质量高(占用内存少,运行速度快)。第十七页,共74页。8.3.1指向数组元素(yuánsù)的指针例如(lìrú):inta[10];int*p;...p=&a[0];指向数组元素(yuánsù)的指针变量,其定义与普通指针变量的定义相同。把元素a[0]的地址赋给p,即p指向数组a的第0个元素。因为数组名表示数组的首地址,所以它等价于p=a;应注意数组a并不代表整个数组,而是把数组a的首地址赋给指针变量p,不是把数组a的各个元素赋给p。
指针变量定义的同时可以赋初值:inta[10];int*p=&a[0];定义时也可以写成如下形式:int*p=a;相当于int*p;p=&a[0];a数组a[0]a[1]a[2]a[i]p*(p+i)a[10]&a[0]第十八页,共74页。假设已经定义了数组和一个(yīɡè)指针变量inta[10];int*p=a;...*p=1;数组元素的引用可以用1.下标法:a[i]2.指针(zhǐzhēn)法:*(a+i)或*(p+i)其中p为指向数组a的指针(zhǐzhēn)变量,初值p=a;下标法比较直观,程序容易阅读指针(zhǐzhēn)表示8.3.2通过指针引用数组元素对p当前所指的元素赋整数值1注意:C规定:p+1(或p++)指向同一数组的下一个元素
(不是地址值简单加1)。p+1代表的实际地址是p+1×d(d是一个数组元素所占的字节数)。当p的初值为a的首地址时,p+i
或a+i
就是a[i]的地址,因为a代表了数组的首地址。*(p+i)或*(a+i)所指的数组元素就是a[i]的内容。
实际上,[]是变址运算符,对a[i]的处理,是将a[i]按a+i计算地址,然后找出此地址单元中的值。指向数组的指针变量也可以带下标,如:p[i]
等价于*(p+i)
,即a[i]
。a数组a[0]a[1]a[2]a[i]p+1或a+1pp+2或a+2p+i或a+i*(p+i)p+9或a+9a[9]第十九页,共74页。例8.5输出(shūchū)数组中全部元素:四种方法。main(){inta[10]={1,2,3,4,5,6,7,8,9,10}; int*p,i;printf("\n下标(xiàbiāo)法:\n");for(i=0;i<10;i++)printf("%d",a[i]);printf("\n指针法(数组名):\n");for(i=0;i<10;i++)printf("%d",*(a+i));printf("\n指针法(指针变量):\n");p=a;for(i=0;i<10;i++)printf("%d",*(p+i));printf("\n指针法(指针变量):\n");for(p=a;p<a+10;p++)printf("%d",*p);printf("\n");}否!a++的操作是非法(fēifǎ)的,因a是常量。能否改成如下形式?for(p=a;a<p+10;a++)printf("%d",*a)几种方法的比较:下标法和指针法(数组名)执行效果是相同的。编译系统先将a[i]转换为*(a+i),即先计算元素地址,因此这两种方法找数组元素比较费时。指针变量法是直接指向数组元素,p++的自加操作比较快。用下标法比较直观。第二十页,共74页。例8.6通过(tōngguò)指针变量输入输出数组a的10个元素main(){int*p,i,a[10]; p=a; for(i=0;i<10;i++) scanf("%d",&a[i]); printf("\n");p=a; for(i=0;i<10;i++) printf("%d",*p++);}如果输出(shūchū)时没有语句p=a;程序将?P是地址(dìzhǐ),不要加&出错,因输入时已改变了指针p的指向。
p++a数组a[0]a[1]a[i]p++pa[9]第二十一页,共74页。(1)p++(或p+=1),使p指向下一个元素,即a[1]。(2)*p++,相当于*(p++),先执行*p,后执行p++。(3)*p++和*(++p)作用不同。*p++是取出a[0]的值,后p++;*(++p)是先使p加1,再取*p,即取出a[1]的值。(4)(*p)++,表示p所指向的元素值加1,即(a[0])++,如若(rúruò)a[0]=3,则(a[0])++的值为4。(5)如果p指向a数组中第i个元素,则:*(p--)相当于a[i--],先对p进行“*”运算,再使p--。*(++p)相当于a[++i],先使p++,再作“*”运算。*(--p)相当于a[--i],先使p--,再作“*”运算。注意:指针(zhǐzhēn)变量的运算。若p=a;则:第二十二页,共74页。一、多维数组的地址例.二维数组inta[3][4]={{1,3,5,7},{9,11,13,15},{17,19,21,23}}; 分析二维数组的性质(xìngzhì):①a是一个数组名,包含3个元素:a[0],a[1],a[2]。每个元素又是一个一维数组,包含4个元素。a[0]a[1]a[2]a数组aa+1a+2(2008)(2000)(2016)8.3.4指向(zhǐxiànɡ)多维数组的指针和指针变量a[0]a[1]a[2]a2321191715131197531②从二维数组的角度看,a代表整个二维数组的首地址,也就是第0行的首地址。若a的首地址为2000,因为每一行4个元素,占用(zhànyònɡ)8个字节,所以则a+1的首地址为2008,a+2的首地址为2016。故a+1代表第1行的首地址,即a[1]的地址;a+2代表第2行的首地址,即a[2]的地址。第二十三页,共74页。④由于(yóuyú)a[0]与*(a+0)等价,a[1]与*(a+1)等价,a[i]与*(a+i)等价,因此a[i]+j与*(a+i)+j等价,它们都是元素a[i][j]的地址。故:元素a[i][j]的地址表示是: a[i]+j或*(a+i)+j而*(*(a+i)+j)或*(a[i]+j)是元素a[i][j]的值。2000200220042006135720082010201220149111315201620182020202217192123aa[0]a[1]a[2]a[0]a[0]+1a[0]+2a[0]+3③a[0]、a[1]、a[2]既然(jìrán)是一维数组名,则它们应代表一维数组的首地址。 a[0]代表第0行一维数组中第0列元素的地址,即&a[0][0];a[0]+1代表第0行第1列元素的地址,即&a[0][1]……。 a[1]代表第1行一维数组中第0列元素的地址,即&a[1][0];……。 a[2]代表第2行一维数组中第0列元素的地址,即&a[2][0]。……。第二十四页,共74页。表10.1表示形式含义地址二维数组名,数组首地址,0行首地址2000
第0行第0列元素地址2000第1行首地址2008第1行第0列元素地址2008第1行第2列元素地址2012
第1行第2列元素的值元素值为13aa[0]+0,*(a+0),*aa+1,&a[1][0]a[1]+0,*(a+1)a[1]+2,*(a+1)+2,&a[1][2]*(a[1]+2),*(*(a+1)+2),a[1][2]aa+1a+22022232020212018192016172014152012132010112008920067200452002320001a[0]+3a[0]+2a[0]+1a[0]+0牢记:①二维数组名(如a)是指向行的,因此a+1中的“1”表示一行元素。一维数组名(如a[0]、a[1])是指向列的,a[0]+1中的“1”代表(dàibiǎo)一个元素。②在行指针前面加一个*,就转换为列指针。如a、a+1(行)→*a、*(a+1)(列)在列指针前面加一个&,就转换为行指针.如a[0](列)→&a[0](行,等价于&*a,即与a等价)第二十五页,共74页。例8.11输出(shūchū)二维数组有关的值。#defineFORMAT"%d,%d\n”main(){inta[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};printf(FORMAT,a,*a); 1printf(FORMAT,a[0],*(a+0)); 2printf(FORMAT,&a[0],&a[0][0]); 3printf(FORMAT,a[1],a+1); 4printf(FORMAT,&a[1][0],*(a+1)+0); 5printf(FORMAT,a[2],*(a+2)); 6printf(FORMAT,&a[2],a+2); 7printf(FORMAT,a[1][0],*(*(a+1)+0)); 8}(假设(jiǎshè)系统给数组a分配的首地址为2000)则运行结果是:注意:a是二维数组名,代表数组首地址,不能企图用*a来得到a[0][0]的值。*a相当于*(a+0),即a[0],它是第0行地址。a是行指针,*a是列指针,指向(zhǐxiànɡ)0行0列元素。**a是0行0列元素的值。a+1指向(zhǐxiànɡ)第一行首地址,不能企图用*(a+1)得到a[1][0]的值,而应该用**(a+1)求a[1][0]元素的值。2000,20002000,20002000,20002008,20082008,20082016,20162016,20169,9 第二十六页,共74页。例8.12用指针变量输出(shūchū)数组元素的值。main(){inta[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};int*p;for(p=a[0];p<a[0]+12;p++){
printf("%4d",*p);}} 运行(yùnxíng)结果:1357911131517192123p-a[0]:为相距(xiāngjù)的单元个数,不是字节数若:a[0]——2000p——2008则:p-a[0]=41.指向数组元素的指针变量二、指向多维数组的指针变量21 23191711 13 1597531paif((p-a[0])%4==0) printf("\n");第二十七页,共74页。数组元素在数组中的位置: 计算某个指定(zhǐdìng)的数组元素在数组中的位置(即相对于数组起始位置的相对位移量):设a是大小为n×m的二维数组,则元素a[i][j]在数组中的相对位置的计算公式为: i*m+j若p是一个指针变量,指向二维数组a的首地址:p=&a[0][0]或p=a[0],则元素a[i][j]的地址为p+i*m+j,故a[i][j]可以表示成:*(p+i*m+j)。如a[1][2]位置(wèizhi)是:1*4+2如a[1][2]地址是:p+1*4+2则a[1][2]的值是:*(p+1*4+2)a[1][2]mna,p 以前的例子都是输出数组全部元素,若是输出某个(mǒuɡè)指定的数组元素该怎么办呢?应该事先计算出该元素在数组中的位置。第二十八页,共74页。2.指向由m个整数组成(zǔchénɡ)的一维数组的指针变量——数组指针由于*运算符的级别低于[],为了定义指针变量p,就必须表示成(*p)
的形式。定义一个数组指针(zhǐzhēn)变量p,指向包含4个元素的一维数组。
定义方法:
int(*p)[4];a[0]a[1]a[2]a数组pp+1p+2*p有4个整型元素,即指针
p指向含有4个整型元素的一维数组,p的值就是该一维数组的首地址,p是数组指针。(*p)[3](*p)[2](*p)[1](*p)[0]p*p(数组)若定义(dìngyì): inta[3][4]; p=&a[0];/*p指向a[0]*/则p+1不是指向a[0][1],而是a[1],p的增值以一维数组的长度为单位。故a[i][j]可表示为:*(*(p+i)+j)。第二十九页,共74页。例8.13输出二维数组任一行任一列(yīliè)元素的值。main(){inta[3][4]={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]=%d\n",i,j,*(*(p+i)+j));}pp+21357911131517192123运行(yùnxíng)结果:输入:1,2<CR>输出:a[1][2]=13说明:a[i][j]的地址可以表示(biǎoshì)为:*(p+i)+j。 注意*(p+i)+j不能写成p+i+j。对于数组指针p,p=a;则p指向二维数组a的第一行首地址。对于指针变量p,p=a[0];则p指向二维数组a的第一行第一列元素首地址,而p=a;是不合法的。第三十页,共74页。voidaverage(float*p,intn){float*p_end;floatsum=0,aver;p_end=p+n-1;for(;p<=p_end;p++)sum=sum+(*p);aver=sum/n;printf("average=%6.2\n",aver);}voidsearch(float(*p)[4],intn){intj;printf("thescoreofNo.%dare:\n",n);for(j=0;j<4;j++)printf("%5.2f",*(*(p+n)+j)
);}由于p指向一维数组,因此(yīncǐ),*(p+n)是第n行的首地址,*(p+n)+j是第n行第j列元素的地址。运行(yùnxíng)结果:average=82.25thescoreofNo.2are:90.0099.00100.0098.00最后一个(yīɡè)元素的地址从指向第一个元素起,依次指向下一个元素第三十一页,共74页。例8.15在上例中,找出有一门以上不及袼的学生,输出(shūchū)其全部成绩。voidsearch(float(*p)[4],intn){inti,j,flag;for(j=0;j<n;j++){flag=0;for(i=0;i<4;i++)
if(*(*(p+j)+i)<60) flag=1;if(flag==1){printf("No.%dfails,hisscoresare:\n",j+1); for(i=0;i<4;i++)printf("%5.1f",*(*(p+j)+i));}}}程序运行结果(jiēguǒ):No.1fails,hisscoresare:65.057.070.060.0No.2fails,hisscoresare:58.087.090.081.0第三十二页,共74页。8.4.1字符串表示形式在C程序中,可以用两种方法(fāngfǎ)访问一个字符串。1.用字符数组存放一个字符串例8.16main(){charstring[]="IloveChina!";printf("%s\n",string);}运行(yùnxíng)结果:IloveChina!string
I
string[0]string[13]loveChina!\08.4字符串的指针和指向(zhǐxiànɡ)字符串的指针变量字符串常量按字符数组处理,在内存中开辟了一个字符数组来存放字符串常量这里string是数组名,代表这个字符数组的首地址。string[i]就是*(string+i)。第三十三页,共74页。2.用字符指针指向(zhǐxiànɡ)一个字符串例8.17main(){char*string="IloveChina!";printf("%s\n",string);}注意:①不能认为string是一个字符串变量,以为是在定义时把“IloveChina!”赋给该字符串变量。②通过(tōngguò)字符数组名或字符指针变量可以输出一个字符串(%s的格式),但是不能企图用数组名输出它的全部元素(%d只能输出一个元素值)。③由于可以用数组或指针来表示字符串,因此对字符串中字符的存取可以用下标法,也可以用指针法。等价于:char*string;string="IloveChina!";定义一个指向(zhǐxiànɡ)字符数据的指针,把该字符串的首地址赋给string定义一个指针变量string,指向字符串的首地址。第三十四页,共74页。例8.18复制字符串a到字符串b。用下标(xiàbiāo)方法:main(){chara[]="Iamaboy.";charb[20];inti;for(i=0;*(a+i)!='\0';i++)*(b+i)=*(a+i);*(b+i)='\0';/*复制(fùzhì)‘\0’*/printf("stringais:%s\n",a);printf("stringbis:");for(i=0;b[i]!='\0’;i++)printf("%c",b[i]);printf("\n");}main(){chara[]="Iamaboy.";charb[20];char*p1,*p2;inti;for(p1=a,p2=b;*p1!='\0’;p1++,p2++)*p2=*p1;
*p2='\0'; /*复制‘\0’*/printf("ais:%s\n",a);printf("bis:");/*逐个输出b中的元素*/for(i=0;b[i]!='\0’;i++)printf("%c",b[i]);printf("\n");}
例8.19
用指针方法:运行(yùnxíng)结果:stringais:Iamaboy.stringbis:Iamaboy.第三十五页,共74页。字符(zìfú)指针作函数参数用函数调用实现(shíxiàn)字符串的复制(1)用字符(zìfú)数组作参数#include<stdio.h>voidmain(){voidcopy_string(charfrom[],charto[]);chara[]=″Iamateacher.″;
charb[]=″youareastudent.″;
printf(“stringa=%s\nstringb=%s\n″,,b);
printf(“copystringatostringb:\n”);
copy_string(a,b);
printf("\nstringa=%s\nstringb=%s\n",a,b);
}voidcopy_string(charfrom[],charto[]){inti=0;
while(from[i]!=′\0′){to[i]=from[i];i++;}
to[i]=′\0′;}第三十六页,共74页。(2)形参用字符(zìfú)指针变量#include<stdio.h>voidmain(){voidcopy_string(char*from,char*to);char*a=″Iamateacher.″;
char*b=″youareastudent.″;
printf("stringa=%s\nstringb=%s\n″,a,b);
printf("copystringatostringb:\n");
copy_string(a,b);
printf("\nstringa=%s\nstringb=%s\n",a,b);
}第三十七页,共74页。voidcopy_string(char*from,char*to){for(;*from!=′\0′;from++,to++)*to=from;*to=′\0′;}(3)对copystring函数(hánshù)还可作简化将copy_string函数(hánshù)改写为voidcopy_string(char*from,char*to){while((*to=*from)!=′\0′){to++;from++;}}第三十八页,共74页。•copy_string函数(hánshù)的函数(hánshù)体还可改为{while((*to++=*from++)!=′\0′);}•copy_string函数(hánshù)的函数(hánshù)体还可写成{while(*from!=′\0′)*to++=*from++;*to=′\0′;}第三十九页,共74页。•上面的while语句还可以(kěyǐ)进一步简化为下面的while语句:while(*to++=*from++);它与下面语句等价:while((*to++=*from++)!=′\0′);将*from赋给*to,如果赋值后的*to值等于′\0′则循环终止(′\0′已赋给*to)•函数体中while语句(yǔjù)也可以改用for语句(yǔjù):for(;(*to++=*from++)!=0;);或for(;*to++=*from++;);第四十页,共74页。•也可用指针(zhǐzhēn)变量,函数copy_string可写为voidcopy_string(charfrom[],charto[]){char*p1,*p2;p1=from;p2=to;while((*p2++=*p1++)!=′\0′);}第四十一页,共74页。2.初始化与赋值方式。字符指针(zhǐzhēn)变量可以在定义时初始化,也可以用赋值语句赋值。如,char*a="IloveChina!";与 char*a; a="IloveChina!"; 等价。8.4.3字符指针变量(biànliàng)与字符数组的讨论将字符串的首地址(dìzhǐ)赋给该指针变量。两者都能用于字符串的存储和运算,但是两者是有区别的:字符数组由若干元素组成,每个元素中存放一个字符;字符指针变量中存放的是字符串的首地址,决不是将字符串放到字符指针变量中。不合法,二者不等价
合法如,charstr[14]="IloveChina!";
charstr[14]; str[]="IloveChina!";对于字符数组,可以在定义时整体赋初值,但不能用赋值语句整体赋值。第四十二页,共74页。3.数组一经(yījīng)定义,就有了确定的内存单元。字符指针变量定义后,虽然也分配了内存单元,在没有对指针变量赋初值之前,这个单元中没有一个确定的地址值,因此该指针并未具体指向某个字符数据。可以有时可以尽量不要用,因a没有确定的指向,程序运行时可能会破坏其它程序。charstr[10];scanf("%s",str);
char*a;scanf("%s",a); 第四十三页,共74页。4.数组名代表该数组的起始地址,它的值不能改变;指针(zhǐzhēn)变量的值可以改变例8.21main(){charstr[]="IloveChina!";
str=str+7;printf("%s",str);}main(){char*a="IloveChina!";intj;printf("Thesixthcharacteris%c\n",a[5]);for(j=0;a[j]!='\0';j++) printf("%c",a[j]);}运行(yùnxíng)结果:ThesixthcharacteriseIloveChina!main(){char*a="IloveChina!";a=a+7;printf("%s",a);}若定义(dìngyì)了一个指针变量,并使它指向一个字符串,就可用下标形式引用指针变量所指的字符串中的字符。例8.22*(a+j)运行结果:China!不合法第四十四页,共74页。5.可以用指针变量指向一个(yīɡè)格式字符串,用于代替printf函数中的格式字符串。例如:char*format;format="a=%d,b=%f\n";printf(format,a,b);charformat[]="a=%d,b=%f\n";printf(format,a,b);它相当于 printf("a=%d,b=%f\n",a,b);因此,只要改变(gǎibiàn)指针变量format所指向的字符串,就可以改变(gǎibiàn)输入输出的格式。这种printf函数称为可变格式输出函数。也可以用字符数组实现。如由于不能用赋值语句对数组整体赋值,所以使用指针变量(biànliàng)的方式较方便。第四十五页,共74页。指向函数(hánshù)的指针变量定义的一般形式: 数据类型标识符(*指针变量名)();例如(lìrú): int(*p)();p所指向(zhǐxiànɡ)的函数的返回值的类型(即函数值的类型)(*p)表示p先与*结合,即说明p是指针变量,然后再与后面的()结合,表示指针p指向函数。main(){intmax(int,int);
int(*p)();inta,b,c;
p=max;scanf("%d,%d",&a,&b);
c=(*p)(a,b);
printf("a=%d,b=%d,max=%d",a,b,max);}例8.23中的main函数改写为:c=max(a,b);第四十六页,共74页。注意:1.这里(*p)()只表示定义了一个存放函数入口地址的指针变量,在把某个函数的地址赋给它之前,并不指向哪个函数,在程序中把某个函数的地址赋给它后才指向这个函数,因此,一个函数指针变量可以先后(xiānhòu)指向不同的函数。2.函数指针变量赋初值:只需给出函数名而不必给出参数 如:p=max;3.用指针变量调用函数时,只用(*p)代替函数名即可。 如:c=(*p)(a,b);4.指向函数的指针变量的算术运算没有意义,例如:p++,p+n,p--这里(zhèlǐ)*p外面的()不能省a,b是实参intmax(x,y)intx,y;{return(x>y?x:y);}调用p所指向(zhǐxiànɡ)的函数第四十七页,共74页。8.7.1指针数组指针数组——数组元素均为指针类型数据,即每个元素都是指针变量(biànliàng)。指针数组的定义形式: 类型标识*数组名[数组长度]例如:int*p[4]例如:若干个字符串的排序和查找。由于字符串本身就是一个字符数组,而各个字符串的长度又不同,因此用二维数组处理不太方便。如果用指针数组,使各元素分别指向各个字符串,那么(nàme)只要改变指针数组中各元素的指向,就可对所指的字符串进行排序。注意(zhùyì)与int(*p)[4]的区别!8.7指针数组和指向指针的指针由于[]的优先级高于*,p先与[]结合表示数组,然后p再与*结合,表示数组是指针类型,int表示每个数组元素指向一个整型变量.第四十八页,共74页。例如(lìrú):有若干字符串,长短不一
FollowmeBASICGreatWallFORTRANComputerdesignFollowme\0BASIC\0Greatwall\0FORTRAN\0Computerdesign\0
Followme
BASIC
GreatWallFORTRANComputerdesignname[0]name[1]name[2]name[3]name[4]指针数组name[5]字符串可以分别定义一些字符串,然后用指针数组中的元素(yuánsù)分别指向各字符串。如果想对字符串排序,不必改动字符串的位置,只需改动指针数组中各元素(yuánsù)的指向。这样,各字符串的长度可以不同,且移动指针要比移动字符串所花的时间少得多。第四十九页,共74页。voidprint(char*name[],intn){inti;for(i=0;i<n;i++)printf("%s\n",name[i]);}#include<string.h>main(){voidsort(char*name[],intn);voidprint(char*name[],intn);char*name[]={"Followme","BASIC","GreatWall","FORTRAN","Computerdesign"};intn=5;/*字符串个数*/
sort(name,n);
print(name,n);}voidsort(char*name[],intn){char*temp;inti,j,k;for(i=0;i<n-1;i++){k=i;for(j=i+1;j<n;j++)if(strcmp(name[k],name[j])>0) k=j;if(k!=i){temp=name[i];name[i]=name[k];name[k]=temp;}}}例8.27将n个字符串按字母(zìmǔ)顺序(由小到大)排序输出(设n=5)第五十页,共74页。运行(yùnxíng)结果:BASICComputerdesignFORTRANFollowmeGreatWallname[0]Followmename[1]BASICname[2]GreatWallname[3]FORTRANname[4]Computerdesign第五十一页,共74页。定义形式:类型标识(biāozhì)**变量名例如:char**p由于*是从右到左结合(jiéhé),因此**p相当于*(*p)表示p是指针变量表示指针变量p指向的还是一个指针,即:*p也是指针变量表示指针变量p指向一个字符型指针变量char*(*p)&*p&ch'B'p*pch**p得到字符
B8.7.2指向(zhǐxiànɡ)指针的指针第五十二页,共74页。例如(lìrú):char*name[5];char**p;p=name+2;printf("%o\n",*p);printf("%s\n",*p);例8.28使用(shǐyòng)指向指针的指针main(){char*name[]={"Followme","BASIC","GreatWall", "FORTRAN","Computerdesign"};
char**p;intj;
p=name;for(j=0;j<5;j++) printf("%s\n",*(p+j));}以%o输出(shūchū)name[2]的值(地址)以%s输出name[2]的字符串"GreatWall"运行结果:FollowmeBASICFORTRANGreatWallComputerdesign第五十三页,共74页。main(){inta[5]={1,3,5,7,9};int*num[5]={&a[0],&a[1],&a[2],&a[3],&a[4]};int**p,j;p=num;for(j=0;j<5;j++){printf("%d\t",**p); p++;}}例8.29运行(yùnxíng)结果:1 3 5 7 9地址值
值地址1地址2值地址1地址2地址3地址n值。。。。。指针变量1指针变量2指针变量3指针变量n变量单级间址二级间址多级间址第五十四页,共74页。8.8.1指针类型(lèixíng)的小结8.8指针类型和指针运算(yùnsuàn)的小结定义含义inti;定义整型变量iint*p;p为指向整型数据的指针变量inta[n]定义整型数组a,它有n个整型元素int*p[n];定义指针数组p,它有n个指向整型数据的指针元素组成int(*p)[n]p为指向含n个元素的一维数组的指针变量intf();f为带回整型函数值的函数int*p();p为带回一个指向整型数据的指针的函数,int(*p)();p为指向函数的指针,该函数返回一个整型数据int**p;p是一个指针变量,它指向一个指向整型数据的指针变量第五十五页,共74页。指针运算有如下几种:1.指针变量可以和整数进行加(减),例如: p++,p--,p+i,p-i,p+=i,p-=i;注意:p+1指的是加1个单位,即p所指向的变量占用的内存(nèicún)字节数,如p是指向整型变量的指针,则加2个字节。2.指针变量可以赋值(赋地址),例如: p=&a;p=array;(array[是数组名)或p=&array[3];p=max;(max是函数名)p1=p2;(p2是指向某个变量的指针,赋给指针变量p1)p=1000;是不允许的。3.指针变量可以赋空值,即不指向任何变量。例如:p=NULL;(NULL表示整数0),在stdio.h中有定义:#defineNULL0任何指针变量或地址都可以与NULL作相等或不相等的比较,如if(p==NULL)…4.两个指针变量可以相减,即p2-p1,表示两个指针之间的元素个数。 但p1+p2无意义。5.若两个指针指向同一个数组的元素,则可以进行比较, 例如:p1<p2,p1<=p2…..p1p28.8.2指针(zhǐzhēn)运算小结第五十六页,共74页。它可以用来(yònɡlái)表示指向一个抽象的数据类型。例如:char*p1;void*p2;……p1=(char*)p2;把p2强制转换为指向字符变量的指针。也可以将一个函数定义为(void*)类型,例如:void*fun(charch1,charch2)指针是C语言中重要的概念。使用指针的优点是:(1)提高效率;(2)被调函数可以改变主调函数的值,即可以返回(fǎnhuí)多个值;(3)可以实现动态存储分配。8.8.3void指针(zhǐzhēn)类型第五十七页,共74页。指针变量如何定义,指针和地址的关系,如何对指针进行运算?如何定义和使用以下对象:指针数组、数组指针、指针的指针、函数(hánshù)指针、返回值为指针的函数(hánshù)。如何使用字符指针?如何通过指针来改变函数(hánshù)实在参数的值?什么是命令行参数?如何正确使用?本章要点第五十八页,共74页。
习题八(一)(一)填空(1)写出建立图一所示的存储结构(jiégòu)所需的定义语句: char____________________.(2)写出图一所示的存储结构(jiégòu)所需的赋值语句: _________='B';p=________________.(3)写出用指针变量p输出字符变量的语句: printf("%c\n",————);(二)填空(1)写出建立图二所示的存储结构(jiégòu)所需的定义语句: int_____,_____________________________;(2)写出图二所示的存储结构(jiégòu)所需的赋值语句: p=________________;
(3)写出用指针变量p输出数组s每一个元素值的语句: for(j=0;j<6;j++)printf("%d\t",__________________);(4)要使p指向数组的最后一个元素,改变p的表达式是—————————。'B'p指针变量字符变量(图一)01491625(图二)数组s[6]指针p第五十九页,共74页。(三)填空(1)写出建立(jiànlì)图三所示的存储结构所需的定义语句: float___________,_________,_____________;(2)写出图三所示的存储结构所需的赋值语句: k=___________;s=____________;p=___________;(3)用指针变量s输出k的值语句:——————;(4)用指针变量p输出k的值的语句:———————;1.34psk(图三)第六十页,共74页。(四)写出以下(yǐxià)程序运行结果。(1)#include<stdio.h>main(){inta,*ap;a=100;ap=&a; printf("%d,%d\n",a,*ap); a=200; printtf("%d,%d\n",a,*ap); *ap=300; printf("%d,%d\n",a,*ap);}(2)#include<stdio.h>main(){chars[]="hello"; printf("%s",s+2);}(3)inta[]={5,6,7,8,10};main(){int*p=a+3; printf("%d,%d,%d\n",*p,*(p+1),*p+1);}(4)main(){intc[]={1,7,12},*p; p=c;
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2026年国开电大计算机网络安全技术作业课堂知识点测验答案
- 人工智能+知识产权保护机制创新研究报告
- 白酒制造业行业分析报告
- 铸造行业产能限值分析报告
- 落实新战略演讲稿高中
- 2026年秋招:陕西汽车试题及答案
- 2026年湖北谷城审计局招聘考试练习试卷(含答案)
- 2026年山西医保局消防安全管理员招聘考试练习试卷(含答案)
- 2026年安徽滁州交通局招聘考试练习试卷(含答案)
- 2026年江苏应急管理局审计专员招聘考试练习试卷(含答案)
- 成人健康体检项目指引(2025年版)
- 检修维护部危险源辨识与风险管控培训
- 铝合金门窗安装施工工艺
- 小学三年级写字教案
- (正式版)T∕CSES 208-2025 珍稀濒危鱼类水质基准推导技术指南(试行)
- 2026年外国法制史考试押题卷及完整答案详解【必刷】
- 12《祝福》课件 统编版高一语文必修下册
- 中风康复期中医护理常规
- 客房卫生处罚制度
- 细胞培养虚拟仿真实验教学的应用
- 上海健康医学院《大学英语》2023-2024学年第一学期期末试卷
评论
0/150
提交评论