1C语言程序设计A课程辅导(5)---第5章指针主要内容:一、指针的概念二、指针变量三、指针运算四、指针与数组五、动态存储分配六、使用指针和动态存储分配的程序举例一、指针的概念指针:计算机内部存储器中一个存储(单元)字节的地址。该地址是一个32位的二进制编码,所占有的字节数(指针类型长度)与int类型长度相同。指针是一种特殊的数据,即不是字符和常数,而是内存单元的编码(地址)。指针与数据类型:在C语言中每个指针都与一个具体的数据类型相联系,即为内存中存储该类型的一个数据的地址,具体为数据所占用存储空间(又称为存储单元或数据单元)的首字节地址。如intx=10;则x的地址就是一个指针,它是存储x的值10的4个存储字节的第一个字节的地址。指针类型:就是它所指向的存储单元中所存数据的类型。如一个指针指向一个整型数(即所指向的存储单元中存储的是整数),则称为整型指针;若指向一个字符,则成为字符指针。数据地址的表示:在一个变量前加上&字符,就表示取该变量的地址,该地址(指针)的类型就是该数据的类型。如intx;&x就表示x的地址,其类型为int指针类型,可表示为int*。空指针:其值为0的指针称为空指针。空指针不指向任何数据,在stdio.h头文件中已经把NULL定义为0,所以符号常量NULL也表示为空指针。二、指针变量能够保存存储单元地址的变量。一般变量保存的是字符或常数。定义格式:类型关键字*指针变量名[=指针表达式],...;与一般变量定义语句格式基本相同,只要在变量标识符前加上*即为指针变量。格式举例:(1)intx,*p;//定义x为int型一般变量,p为int*型指针变量(2)inta=10,*pa=&a;//定义int变量a并初始化10,同时定义pa为//int*类型的指针变量,并初始化为a的地址,即整数变量的地址(3)char*p1=0,*p2=pointer;//p1为空,p2指向字符串pointer//把pointer的第1个字符的地址赋给指针变量p2,类型为char*(4)char*d[5]={0};//字符数组d[5]中每个元素均为指针变量,其值为空2(5)intx=5,*px=&x,**pp=&px;//px:int*类型,pp:int**类型pppxx&px&x5//px的地址具有int**类型(6)char*rp[3]={front,middle,rear};//定义指针数组和初始化(7)inta[10],*ps=a;//数组名a的值是第1个元素的地址,//即a[0]元素的地址&a[0],类型为int*输出一般指针和输出字符指针所指向的字符串:输出一般指针时使用的格式符为%p,输出字符指针所指向的字符串时使用的格式符为%s。例如:#includestdio.hvoidmain(){inta=30;char*cp;//能够保存一个字符变量或数组元素的地址cp=output;//cp指向字符串常量outputprintf(%p%p\n,&a,cp);//输出指针,分别为a和'o'的地址printf(%d%s\n,a,cp);//输出a的值和cp所指向的字符串}运行结果:0013FF7C0042002C//十六进制8位30output向字符指针输入字符串:chara[30];scanf(%s,a);//gets(a);chara[30],*p=a;scanf(%s,p);//gets(p);利用typedef语句定义指针数据类型:(1)typedefint*inPointer;//定义inPointer为int*类型,//若用inPointers1=NULL;等价于int*s1=NULL;(2)typedefchar*chPointer;//定义chPointer为char*类型(3)typedefchar*AA[10];//定义AA为char*[10]指针数组类型,//若用AAaa;等价于char*aa[10];三、指针运算包括赋值、取地址、间接访问、增减1等。赋值(=)双目运算,把赋值号右边的指针(地址)值赋给左边的同类型的指针变量。intx=30,*px,*py;px=&x;py=px;//把x的地址赋给px,把px值赋给py,它们均指向x取地址(&)单目运算,使用在一般变量的前面,表示取变量的地址。charch='+',*p=&ch;//取ch变量的地址赋给p,p指向chchars[30]=Apple,*ps=s;//字符数组s的地址赋给ps,ps指向s,//s和ps的值均为s数组中第1个元素'A'的存储地址间接访问(*)3在指针前面使用*运算符能够访问到指针所指向的对象。如:intx=12,*px=&x;//px指向x,px所指向的对象x的值为12printf(%d\n,*px+10);//22,使用*px+10等同于x+10*px+=15;//27,使用*px+=15等同于x+=15,*px即x的值变为27增1(++)和减1(--)单目运算,可使用在指针变量的前面或后面,使指针变量的值增1或减1。inta[5]={10,15,20,25,30},x,y;int*p=a;//p指向a[0]元素,即p的值为a[0]的地址x=*p++;//x的值为10,p加1后赋给p,p指向下一个元素a[1]x=*++p;//x的值为20,p指向元素a[2]y=(*p)++;//y的值为20,a[2]被加1后赋给a[2],a[2]的值变21p--;//运算前p指向a[2],运算后p指向a[1]printf(%d\n,*p--);//输出a[1]的值15,然后p减1后指向a[0]p=&a[3];printf(%d\n,--*p);//*p的值25,输出减1后的a[3]的值24注意:当指针变量p指向数组中的一个元素时,p++或++p则使p指向其下一个元素,p—或—p则使p指向其前一个元素。若p指向a[i],则进行p++或++p运算后,p指向a[i+1];若p指向a[i],则进行p--或--p运算后,p指向a[i-1]。加(+)和减(-)双目运算,使指针向后或向前移动若干个数据位置。intb[6]={1,3,5,7,9,11};int*p=b,*q=p+5;//p指向b[0],q指向b[5],p+5等同于b+5printf(%d\n,*(q-3));//输出b[2]的值5p=b+6;//p指向b[6]数组后面的一个数据单元,即b[5]后的位置*--p;//取b[5]元素的值11加赋值(+=)和减赋值(-=)双目运算,左边的指针变量加上或减去右边相应的值。chara[20]=wbcdefghi;char*p=a;//p指向a[0],即字符wp+=7;//运算后p指向a[7],即字符hp-=4;//运算后p指向a[3],即字符d*p-=3;//运算后使a[3]的值减3,由100(d的ASCII码)变为97(a的码)比较(==,!=,,=,,=)双目运算,比较左右两个指针(地址)的大小。数组中后面元素的指针大于前面元素的指针,即&a[i+1]&a[i]。inta[5]={20,15,32,48,46};int*p;for(p=a;pa+5;p++)printf(%d,*p);//依次输出a中每个元素值for(p=a+4;p=a;p--)printf(%d,*p);//逆序输出a中每个元素值这一讲就到这里,同学们再见!四、指针与数组41.指针与一维数组一维数组名是指向该数组第一个元素的指针常量inta[7]={12,25,18,30,63,44,50};0123456a*a表示a[0]元素,*a的值即为a[0]的值。a的值等于&a[0],类型int*。a为指针常量,即其值不可修改,所以使用a++是错误的。道理很简单,如果允许修改数组名的值,以后就无法访问该数组了。数组元素的下标和指针访问方式a[i]等价于*(a+i),分别为下标和指针访问方式。*a与a[0],*(a+1)与a[1]等价。假定int*p=a+i;则*p与a[i],*p++与a[i++]等价。for(i=0;i7;i++)printf(%5d\n,a[i]);//下标方式等价于:for(i=0;i7;i++)printf(%5d\n,*(a+i));//指针方式等价于:for(p=a;pa+7;p++)printf(%5d\n,*p);//指针方式等价于:for(p=a,i=0;i7;i++)printf(%5d\n,p[i]);//下标方式//p初始指向数组a中第1个元素,p[i]与a[i]等价,均为*(a+i)2.指针与二维数组二维数组名是指向该数组第一行元素的指针常量,其值为整个二维数组空间的首地址intb[3][4]={{1,2,3,4},{2,4,6,8},{3,6,9,12}};b为指向b[0]行4个元素的指针常量,其值为&b[0],亦即&b[0][0]的值,但类型为int(*)[4],而不是int*。b[i]为i行元素(一维数组)的数组名,b[i]的值为b[i][0]元素的地址。它们的类型均为int*。而&b[i]的值虽然也等于b[i][0]元素的地址,但类型为int(*)[4],因为它是该行元素的首地址。同一维数组名一样,不允许修改二维数组名的值,如进行b++操作是错误的。二维数组名的值可以赋给同类型的指针变量int(*pb)[4]=b;//把b的值赋给同类型的指针变量pb,pb也指向此数组,//此后pb也可以作为数组名使用,访问任何元素pb[2][3]等价与b[2][3],值为12。注意:int(*pb)[4]与int(*pb)[5]不同,因为pb所指向的元素个数不同。若int*pb[4]的则定义pb为含有4个元素的整型指针数组,而不是定义指向含有4个整型元素的数组的指针。二维数组元素的下标和指针访问方式b[i][j](*(b+i))[j]或*(b[i]+j)或*(*(b+i)+j)//均等价for(i=0;i3;i++){for(j=0;j4;j++)printf(%d,b[i][j]);//*(*(b+i)+j)printf(\n);}122518306344505使用指针和数组的程序举例例1:#includestdio.hvoidmain(){inta[8]={3,5,7,9,11,13,15,17};inti,*p=a;//p的值为&a[0]for(i=0;i8;i++){printf(%5d,*p++);//先*p,后p的值加1if((i+1)%4==0)printf(\n);//输出4个元素后换行}}输出结果:357911131517例2:#includestdio.hvoidmain(){inta[8]={46,38,72,55,24,63,50,37};intmax=*a,min=*a;//a[0]的值赋给max和minint*p;for(p=a+1;pa+8;p++){//扫描整个数组if(*pmax)max=*p;//大放maxif(*pmin)min=*p;//小放min}printf(%5d%5d\n,max,min);//输出最大和最小值}输出结果:7224五、动态存储分配1.动态存储分配的概念变量和数组在计算机内存中都有对应的存储空间,该存储空间的分配时机有两种:一种在编译阶段,或者说在程序运行前;另一种在程序运行之中。前者称为静态存储分配方式,后者称为动态存储分配方式。变量的静态存储分配方式通过一般的变量定义语句定义的变量,采用的是静态存储分配方式。如:intx,y;//x和y分别对应的4个字节的存储空间是静态分配的inta[10];//数组a所需要的40个字节的存储空间是静态分配的charb[20],*p=b;//数组b的20个字节和指针变量p的4字节是静态分配的doublec[M][N]={{0}};//数组c的M*N*8个字节的存储空间是静态分配的变量的动态存储分配方式变量的动态存储分配需要调用在