《C语言程序设计》课件第七章11_第1页
《C语言程序设计》课件第七章11_第2页
《C语言程序设计》课件第七章11_第3页
《C语言程序设计》课件第七章11_第4页
《C语言程序设计》课件第七章11_第5页
已阅读5页,还剩57页未读 继续免费阅读

下载本文档

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

文档简介

-C语言程序电子教案算法语言程序设计第七章指针

主要内容:指针的概念指针运算指针与函数参数指针与数组字符指针和字符串函数型指针指针型指针指针功能

指针是一种构造类型数据,利用指针可以有效地表示复杂的数据结构,动态地分配内存,方便地使用字符串和数组,灵活地实现函数间的数据传递,直接处理内存地址等。掌握指针的应用,可以使算法表达更确切,程序更清晰简炼,代码更紧凑有效。7.1指针的概念7.1.1存储单元的地址与内容

程序一旦被执行,则该程序的指令、常量和变量等都要存放在机器的内存中。内存是以字节来划分存储单元的,每个字节都有一个编号,这就是存储单元的“地址”。在计算机中,根据内存的地址,就可以访问存储在该单元中的数据。将某存储单元中的数据称为这个存储单元的“内容”。存储单元的地址与内容如下图所示。图7.1…..26518…...内存1000100210042010变量a变量b变量c变量i7.1.2指针和指针变量内存变量的三个基本特征:变量名、变量的内容和变量的存储地址。要访问一个变量,可以有两种方式:用变量名;用变量的地址。7.1.3指针变量的定义指针变量使用前应先定义。定义格式:

类型标识符*标识符说明:

标识符是被定义的指针变量的名字。

类型标识符则表明了该指针变量所指向变量的类型。例如:int*p;char*ch;7.1.4指针运算符和指针变量的引用1.运算符

&取地址运算符运算格式:&标识符*指针运算符(间接访问运算符)

运算格式:*指针变量例如:

int*p,i,j;…p=&i;j=*p;表示把变量i的地址赋给指针变量p表示将指针变量所指向的变量i的内容赋给变量j2.指针变量的引用

指针变量一经定义,可以像其它基本类型变量一样引用。

(1)指针变量只接收地址,例如:

int*pi,*pj,*pk,i,j,k;…pi=&i;pj=&j;pk=&k;

(2)指针变量指向变量后,可以像其它基本类

型变量一样引用。例如:int*pi,*pj,i,j;pi=&i;pj=&j;*pi=0;/*等价于i=0;*/*pj+=1;/*等价于j+=1;*/*(pi)++;/*等价于i++;*/切记:指针变量引用前必须指向某个变量。例如:

#include〈stdio.h〉voidmain(){int*p;*p=100;/*错*/printf("%d\n",*p);}(3)指向同类型的指针变量之间可以相互赋值例如:pi=pj;注意:对于指针变量pi和pj,下面两种赋值的效果是不等价的。

pi=pj;和*pi=*pj;(4)如果指针变量pi已经指向变量i,那么:

&*pi与&i等价。因为两个运算符的优先级别相同,按自右向左方向结合,先进行*pi运算,它就是变量i,再进行&运算,所以&*pi与&i等价。

(5)若i是一变量,则*&i与i等价。

因为先进行&i运算,其结果是地址,再进行*运算,相当于取这个地址中的内容,即i的值。故*&I与i等价。

(6)指针变可以用存储类型说明,对于被说明为静态的和外部的指针变量可以被赋初值(初始化)。例如:int*p,i,*pi=&i;#include〈stdio.h〉voidmain(){staticchara[10],*pa=a;…}

(7)指针变量可以指向任何类型变量,其中

包括其他的指针变量。例如:inti,*pi,**ppi;…pi=&i;ppi=π…指向指针变量的指针变量ppi指针变量pi变量i200010002000地址20001000地址1000

(8)指针变量可以赋“空”值,其含义是该

指针变量不指向任何变量。

“空”值通常作为指针的异常标志。用NULL表示,即全部二进位均为0值。NULL其实就是0,但习惯上不用0而用NULL,办法是通过#define定义。例如:

#defineNULL0#include〈stdio.h〉voidmain(){inti,*p;p=NULL;…p=i;…if(p==NULL)printf("pointerpisNULL");…}

(9)一个指针变量的值为NULL与未对该指针

变量赋值是不同的。

前者是有值的,其值为0,称“空值”,它表示不指向任何变量;而后者虽未对指针变量赋值,但并不等于该指针变量无值,只是它的值是一个不确定的值,即该指针变量正指向某个未知的单元。这时,程序若引用这个指针变量,显然是很危险的。所以,在引用一个指针变量前,必须先要对它赋值。3指针变量引用举例

【例7.1】观察指针变量pi和pj构成的语句(pi=pj;*pi=*pj;)之异同。#include〈stdio.h〉voidmain(){inti,j,*pi,*pj;scanf("%d,%d",&i,&j);pi=&i;pj=&j;printf("i=%d,j=%d\n",i,j);printf("*pi=%d,*pj=%d\n",*pi,*pj);printf("pi=%u,pj=%u\n",pi,pj);pi=pj;printf("i=%d,j=%d\n",i,j);printf("*pi=%d,*pj=%d\n",*pi,*pj);printf("pi=%u,pj=%u\n",pi,pj);pi=&i;pj=&j;*pi=*pj;printf("i=%d,j=%d\n",i,j);printf("*pi=%d,*pj=%d\n",*pi,*pj);printf("pi=%u,pj=%u\n",pi,pj);}程序运行结果如下:2,8i=2,j=8*pi=2,*pj=8pi=65494,pj=65496i=2,j=8*pi=8,*pj=8pi=65496,pj=65496i=8,j=8*pi=8,*pj=8pi=65494,pj=65496【例7.2】输入a、b两个整数,经比较后,按大小顺序输出a和b。#include〈stdio.h〉voidmain(){int*pi,*p2,*p,a,b;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);}程序运行结果如下:9,18a=9,b=18max=18,min=97.1.5指针数组

指针数组:定义为指针型的数组称为指针数组。例:利用指针数组对字符型二维数组输入输出。

#include〈stdio.h〉voidmain(){chara[5][20],*p[5];inti;for(i=0;i<5;i++)p[i]=a[i];for(i=0;i<5;i++)scanf(“%s”,p[i]);for(i=0;i<5;i++)printf(“%s\n”,p[i]);}7.1.6数组行指针

数组行指针:用于指向二位数组的某一行的指针称为数组行指针。格式:int(*p)[m];

说明:p是行指针变量;

m是数组列大小。

数组元素地址*(指针变量+行下标)+列下标

数组元素引用*(*(指针变量+行下标)+列下标)例:#include〈stdio.h〉voidmain(){inta[2][3],(*p)[3]=a;inti,j;for(i=0;i<2;i++)for(j=0;j<3;j++)scanf(“%d”,*(p+i)+j);for(i=0;i<2;i++){printf(“\n”);for(j=0;j<3;j++)printf(“%10d”,*(*(p+i)+j));

}}7.2指针运算

指针:是指向某类型数据的地址。说明:

地址的分配是由C编译系统决定的;

指针变量的值是一具体类型的特定变量地址所允许的整数,但必须明确:指针不是整数。

对指针的运算不能像对整型数据那样进行所有的算术、逻辑和关系运算,而只能进行C

语言所规定的某些运算。

指针运算的值与某一类型数据的地址有关。7.2.1指针的算术运算

设p、q是指针变量,n为一整数,则:p+n、p-n、p++、p--、++p、--p、p-q都是指针变量允许实施的算术运算,它们的意义分别是:

(1)p+n:表示由p所指向位置向高地址移n个位移量。

(2)p-n:表示由p所指向位置向低地址移n个位移量。

(3)p++:将当前指针p向高地址移一个位移量

(4)p--:将当前指针p向低地址移一个位移量。

(5)++p:将当前指针p向高地址移一个位移量。

(6)--p:将当前指针p向低地址移一个位移量。

(7)p-q:表示两个被指向对象间相隔位移量的个数。【例7.3】计算字符串长度函数。strlen(char*s){char*p=s;while(*p!='\0')p++;return(p-s);}strlen(char*s){char*p=s;while(*p)p++;return(p-s);}7.2.2指针的关系运算

设p、q是指向同一数据集合的指针变量,则p<q、p<=q、p>q、p>=q、p==q、p!=q是指针变量允许实施的关系运算。它们的意义分别是:

(1)p>q:若表达式的结果为非0;则表明p指针变量所指向的元素在q指针变量所指向的元素之后。否则,结果为0,则表明

p指向的元素在q指向的元素之前。

(2)p<q:若表达的结果为非0,则表明p所指向的元素在q所指向的元素之前。否则,结果为0,则表明p所指向的元素在q

所指向的元素之后。图7.7内存……..a[0]a[1]a[3]a[2]a[4]a[5]……...(低地址)(高地址)pq7.2.3指针变量的赋值运算

设p,q是指向同一数据类型的指针变量,n为一整数,则:p=q、p=q+n、p=q-n、p+=n,p-=n都是指针变量允许实施的赋值运算。它们的意义分别是:

(1)p=q:将q中的地址值赋给p。

(2)p=q+n:将由q所指位置向高地址移n个位移量后,所得的实际地址值赋给p。

(3)p=q-n:将由q所指位置向低地址移n个位移量后,所得的实际地址值赋给p。

(4)p+=n:等价于p=p+n。

(5)p-=n:等价于p=p-n。指针变量在进行赋值运算时,要注意:

(1)相互赋值的指针变量p、q,它们所指向的变量的类型应一致,否则会出错。

(2)如果p+=x中,x低于int类型级别时,应将x

强制转换成int类型。如x为double型时,写为:p+=(int)x;(3)指针的赋值运算仅上述列出的形式有意义,其他均无意义。更不能将一个整型变量的值或常数赋给指针变量。即:

p=n;p=100;都是错误的。7.2.4指针运算符与自增、自减运算符的混用

指针运算符和自增、自减运算符都属于同优先级左结合的单目运算符。它们的混用在程序设计中较为普遍,尤其是指针运算符*与自增运算符++、自减运算符--的混用更为常见。例如:

while(*p)putchar(*p++);

中的*p++就是指针运算符与自增运算符混用的一种。在这条语句中,*p++的运算结果是:先将*p作为操作数输出,再将指针变量p向高地址自增一个位移量。所以:*p++等价于*p;p+=1;7.3指针与函数参数

函数的参数不仅可以是基本类型的变量,也可以是指针变量。对任何必须以地址方式传送的参数,均可以利用指针来实现。特别是函数的返回值多于一个时,可以利用指针来传递函数的返回值。注:当指针作为函数的参数时,对应的实参必须是变量的地址或指针;其相应的形参应是指针变量。这样才能保证地址的正确传送。【例7.5】将指针作为函数参数,改写[例7.2]

的程序。#include〈stdio.h〉swap(int*p1,int*p2){intp;p=*p1;*p1=*p2;*p2=p;}voidmain(){inta,b;int*pa,*pb;

scanf("%d,%d",&a,&b);pa=&a;pb=&b;if(a<b)swap(pa,pb);printf(”\n%d,%d\n",a,b);}程序运行结果如下:8,1818,8#include〈stdio.h〉swap(int*p1,int*p2){int*p;p=p1;p1=p2;p2=p;}voidmain(){inta,b;int*pa,*pb;scanf("%d,%d",&a,&b);pa=&a;pb=&b;if(a<b)swap(pa,pb);printf(”%d,%d\n",*pa,*pb);}程序运行结果如下:8,188,18#include〈stdio.h〉

swap(int*p1,int

*p2){int*p;p=p1;p1=p2;p2=p;}voidmain(){inta,b;int*pa,*pb;scanf("%d,%d",&a,&b);pa=&a;pb=&b;if(a<b)swap(&a,&b);printf(”\n%d,%d\n",*pa,*pb);}程序运行结果如下:8,188,18#include〈stdio.h〉swap(int**p1,int**p2){int*p;p=*p1;*p1=*p2;*p2=p;}voidmain(){inta,b,*pa,*pb;scanf("%d,%d",&a,&b);pa=&a;pb=&b;if(a<b)swap(&pa,&pb);printf("\n%d,%d\n",*pa,*pb);}程序运行结果如下:

8,1818,8注意:

交换指针所指向变量的值和交换指针值的效果是不同的。

swap(int*p1,int*p2){int*p;p=p1;p1=p2;p2=p;}错误:swap(int*p1,int*p2){int*p;*p=*p1;*p1=*p2;*p2=*p;}7.4指针与数组

在C语言中,指针与数组的关系非常密切。任何能由下标完成的对数组的操作,也可以由指针来实现。使用指针对数组进行运算处理,可使目标程序占用内存少、运行速度快。指针与数组密切的关系主要反映在以下两点:

(1)对数组元素访问时,用数组的下标和用指针是统一的;

(2)当主调函数中的实参是数组名时,被调函数中相应形参数组的定义可以由形参指针变量代替。说明

若程序中定义如下数组和指针变量:

inta[10],*pa;

由于数组名是数组的首地址,一旦将数组名赋给指针变量后,即pa=a;

指针与数组之间就建立了对立关系:数组下标和指针变量运算的对应;数组元素与指针变量的*运算(例如*pa)的对应。设i为数组的下标,n为一整型数,那么;&a[0]≡a≡pa≡&pa[0]&a[i]≡a+i≡pa+i≡&pa[i]

a[0]≡*a≡*pa≡pa[0]a[i]≡*(a+i)≡*(pa+i)≡pa[i]【例7.6】用指针变量访问数组元素。

#include〈stdio.h〉voidmain(){staticinta[]={1,2,3,4,5};int*pa,k;pa=a;k=0;while(k<5){printf("%d,",*(pa+k));k++;}printf("\n");}程序运行结果如下:1,2,3,4,5,【例7.7】用形参指针变量的定义代替形参数组的

定义。#include〈stdio.h〉voidfun(char*p){while(*p)putchar(*p++);printf(“\n");}voidmain(){staticchara[]="ABCDE";fun(a);}程序运行结果如下:ABCDE

形参数组的定义可以用形参指针变量代替。需要强调的是:

(1)若实参是变量的地址,传给形参指针变量后,这个指针不能作为数组的指针使用。

(2)在函数调用中,实参是数组名时,往往相应的形参用指针变量,这使得有时在函数中会看不出它是一个数组,因此,在函数中引用指针变量时,先要弄清指针变量所指的对象是什么,才不会出错。

(3)指针变量可以用++、--来不断改变自身的值,而数组名不允许进行自增、自减的运算。

(4)要注意指针变量的当前值。尤其是要注意指针初值的恢复。举例#include〈stdio.h〉voidmain(){inta[10],*p,i;p=a;for(i=0;i<10;i++)scanf("%d",p++);printf("\n");for(i=0;i<10;i++)printf("%d,",*p++);}#include〈stdio.h〉voidmain(){inta[10],*p,i;p=a;for(i=0;i<10;i++)scanf("%d",p++);printf("\n");p=a;for(i=0;i<10;i++)printf("%d,",*p++);}错误正确【例7.8】用指针输出多维数组元素#include〈stdio.h〉voidmain(){staticinta[3][3]={{1,2,3},{4,5,6},{7,8,9}};staticintb[2][2][2]={{{1,2},{3,4}},{{5,6},{7,8}}};int*p,i;p=a;for(i=0;i<9;i++){if(i%3==0)printf("\n");printf("%3d",*p++);}printf("\n");p=b;for(i=0;i<8;i++){if(i%2==0)printf("\n");printf("%3d",*p++);}printf("\n");}【例7_9】用指针法输出个别数组元素。#include〈stdio.h〉voidmain(){staticinta[2][3]={1,2,3,4,5,6};int*p;p=a;printf("a[1][2]=%d\n",*(p+1*3+2));printf("a[0][1]=%d\n",*(p+0*3+1));}程序运行结果如下:a[1][2]=6a[0][1]=2指针指向二维数组首地址的数组元素引用:若inta[2][3],*p=a;则数组元素引用a[i][j]≡*(p+i*3+j)7.5字符指针和字符串

C语言对字符串的描述有两种方法:用字符数组描述字符串,或者用字符指针描述字符串。而用字符指针形式描述字符串比使用字符数组有更大的便利性,程序也显得清晰、简炼。通常,用字符数组处理字符串时,要定义一个存储字符串的字符数组,再利用循环对字符数组中的每个元素进行操作。【例7.10】利用字符数组处理字符串。#include〈stdio.h〉voidmain(){staticcharc[11]={"Clanguage"};inti;for(i=0;i<10;i++)printf("%c",c[i]);printf("\n");}程序运行结果如下:Clanguage

对字符串处理的另一种方法是:利用指向字符串的字符指针变量。

【例7.11】用指向字符串的指针处理字符串。#include〈stdio.h〉voidmain(){char*p;p="Clanguage";printf("%s\n",p);}程序运行结果如下:Clanguage【例7.12】用字符指针将一个字符数组中的字

符串拷贝到另一个字符数组中。#include<stdio.h>strcpy(char*p1,char*p2){while(*p2++=*p1++);}voidmain(){staticchara1[]="Clanguage";chara2[11];strcpy(a1,a2);puts(a2);}程序运行结果如下:Clanguage7.6函数型指针7.6.1函数型指针的定义一个函数被执行时,在内存中占据一定的空间,这个空间的起始地址是用函数名表示的,称为函数的入口地址。可以用指针指向这个入口地址,并通过该指针变量来调用这个函数。这种指针变量称为函数型指针变量。

函数型指针变量的一般形式为:

数据类型标识符(*指针变量名)();例如:

int(*f1)();float(*f2)();

定义了两个函数型指针变量f1和f2,f1所指向的函数返回整型类数据,f2所指向的函数返回实型类数据。7.6.2函数型指针的赋值

给函数型指针变量赋值的一般形式为:

函数型指针变量名=函数名例如:有一个C函数min(a,b),若fp为函数型指针变量,则fp=min;的含义为将min

函数的入口地址赋给函数型指针变量

fp,使得fp指向函数min。上述语句不能写成:fp=min(a,b);的形式。对函数的调用就有两种方法来实现。第一种方法,用函数名实现调用:x=min(a,b);第二种方法,用函数型指针实现调用:

fp=min;x=(*fp)(a,b);7.6.3函数型指针的引用

【例7.13】利用函数型指针变量调用函数max,计

算最大值。#include〈stdio.h〉voidmain(){int(*fp)(),max();inta,b,c;scanf("%d,%d",&a,&b);fp=max;c=(*fp)(a,b);printf("max=%d\n",c);}max(intx,inty){intz;z=(x>y)?x:y;return(z);}程序运行结果如下:10,20max=207.7指针型指针

在C语言中,允许一个指针指向另一个指针。如果一个指针变量的内容是另一个指针变量的存储地址,则称该指针变量为指针型指针变量,又称指向指针的指针变量或多级指针变量。指针型指针变量使用前必须先定义。7.7.1指针型指针变量的定义

指针型指针变量定义的一般格式为:类型标识符**标识符;

其中,标识符是被定义的指针型指针变量的名字。类型标识符则表明了被指针型指针变量所指向的指针变量所指向的变量的数据类型。例如:

int**pp,*p,i;

p=&i;pp=&p;【例7.15】指针型指针变量举例。#include〈stdio.h〉voidmain(){inti,*p,**pp;i=8;p=&i;pp=&p;printf("%d\n%d\n%d\n",i,*p,**pp);}程序运行结果如下:8887.7.2指针型指针的引用例7.16用指针型指针实现5门课名字符串的输出。#include〈stdio.h〉voidmain(){staticchar*p[]={"BASIC","Ada","PASCAL","C","FORTRAN"};char**pp;inti;for(i=0;i<5;i++){pp=p+i;printf("%s\n",*pp);}}程序运行结果如下:BASICAdaPASCALCFORTRAN【例7.17】指针型指针用于指针数组元素指

向整型数据的例子。#include〈stdio.h〉voidmain(){staticinta[5]={2,4,6,8,10};staticint*pi[5]={&a[0],&a[1],&a[2],&a[3],&a[4]};int**pp,i;pp=pi;for(i=0;i<5;i++){printf("%d\t%u\n",**pp,pp);pp++;}}结果:241441641842010422#include〈stdio.h〉voidmain(){staticinta[5]={2,4,6,8,10};staticint*pi[5]={&a[0],&a[1],&a[2],&a[3],&a[4]};int**pp,i;pp=pi;for(i=0;i<5;i++){printf("%d\t%u\n",**pp,pp);(*pp)++;}}结果:2414414414414104147.8命令行参数前面使用的主函数main不带参数,即main(

温馨提示

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

评论

0/150

提交评论