C程序设计:第十章 指针_第1页
C程序设计:第十章 指针_第2页
C程序设计:第十章 指针_第3页
C程序设计:第十章 指针_第4页
C程序设计:第十章 指针_第5页
已阅读5页,还剩96页未读 继续免费阅读

下载本文档

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

文档简介

第十章指针指针是C语言中的重要概念,也是C语言的重要特色。使用指针,可以使程序更加简洁、紧凑、高效。10.1指针和指针变量的概念

10.2指针变量的定义与应用10.3数组的指针和指向数组的指针变量

10.4字符串的指针和指向字符串的指针变量10.5返回指针值的函数

10.6指针数组与主函数main()的形参

10.7函数的指针和指向函数的指针变量10.1

指针和指针变量的概念1.内存地址──内存中存储单元的编号(1)计算机硬件系统的内存储器中,拥有大量的存储单元(容量为1字节)。为了方便管理,必须为每一个存储单元编号,这个编号就是存储单元的“地址”。每个存储单元都有一个惟一的地址。(2)在地址所标识的存储单元中存放数据。2.变量地址──系统分配给变量的内存单元的起始地址假设有这样一个程序:

inta;floatb;a=3;b=5;3A00a3A083A073A093A0Ab3A01C编译程序编译到该变量定义语句时,将变量num登录到“符号表”中。符号表的关键属性有两个:一是“标识符名(id)”,二是该标识符在内存空间中的“地址(addr)”。3.变量值的存取──通过变量在内存中的地址进行系统执行“scanf(”%d“,&a);”和“printf(”num=%d\n“,a);”时,存取变量a值的方式可以有两种:

(1)直接访问

(2)间接访问(1)直接访问──直接利用变量的地址进行存取

1)上例中scanf(“%d”,&a)的执行过程是这样的:用变量名a作为索引值,检索符号表,找到变量a的起始地址3A00;然后将键盘输入的值(假设为3)送到内存单元3A00和3A01中。此时,变量a在内存中的地址和值。

2)printf("a=%d\n",a)的执行过程,与scanf()很相似.(2)间接访问──通过另一变量访问该变量的值通过a_pointer访问变量a值的过程如下:3a3A003A00a_pointer4000思考如何定义a_pointer变量?如何获得a的地址?如何通过a_pointer访问a?指针与指针变量(1)指针──即地址一个变量的地址称为该变量的指针。通过变量的指针能够找到该变量。(2)指针变量──专门用于存储其它变量地址的变量指针变量a_pointer的值就是变量a的地址。指针与指针变量的区别,就是变量值与变量的区别。10.2指针变量定义方法

数据类型*指针变量指针说明符指针变量的类型:所指向的内存中所存放的数据类型如:int*p;指针变量的值是地址,是个无符号整数。不能直接将整型常量赋给指针变量。用变量的地址给指针变量赋值。变量的类型必须与指针变量的类型相同。如:char*p_ch=&a;2.赋空值如:Int*p_int=NULL;若不赋值,指针变量的值是随机的。指针变量的赋值:两个有关的运算符---*、&。*指针运算符&取地址运算符含义:&a变量a所占据内存空间的首地址*p指针变量p所指向的内存单元中

的数据如:

int*p,b; p=&a;b=*p;通过指针变量访问所指变量将指针变量指向被访问的变量

inta=3,*p;p=&a;访问所指变量取内容:printf("%d",*p);存内容:*p=100;main()

{intnum=12,*p;

p=#

num=*p+5;

printf(“num=%d,*p=%d\n”,num,*p);

}

定义一个指向int型数据的指针变量p取变量num的地址,赋值给p程序运行结果:num=17,*p=17运算规则*、&优先级相同,且右结合。与++、--、!等单目运算符优先级相同。高于算术运算符。如:inta=2,*p=&a;printf("%d%d",*p++,(*p)++);找出等价的式子inta=2,*p=&a;&*p*&a(*p)++&aa*p++*(p++)a++main(){inta,b,c;int*p1=&a,*p2=&b,*p3=&c;scanf(“%d,%d”,p1,p2);c=++*p1+(*p2)++;printf(“c=%d\n”,*p3);c=(*p1)+++*p2;printf(“c=%d\n”,c);输入:2,3程序运行结果:c=6,c=7使用指针变量求解:输入2个整数,按升序(从小到大排序)输出。

main(){inta,b;int*p1=&a,*p2=&b,*pointer;scanf(“%d”,p1);scanf(“%d”,p2);printf(“a=%d,b=%d\n”,a,b);if(*p1>*p2)

pointer=p1,p1=p2,p2=pointer;printf(“min=%d,max=%d\n”,*p1,*p2);}

4000400864p1p240004008ab4008400064p1p240004008ab10.2.2指针变量作函数参数1.指针变量,既可以作为函数的形参,也可以作函数的实参。2.指针变量作实参时,与普通变量一样,也是“值传递”,即将指针变量的值(一个地址)传递给被调用函数的形参(必须是一个指针变量)。注意:被调用函数不能改变实参指针变量的值,但可以改变实参指针变量所指向的变量的值。2个数比较大小,由小到大排列****************************************************

exchange()功能:交换2个形参指针变量所指向的变量的值

形参:2个,均为指向整型数据的指针变量

返回值:无

****************************************************

voidexchange(int*p1,int*p2)

{inttemp;

temp=*p1,*p1=*p2,*p2=temp;

}

main(){intnum1,num2;

int*num1_p=&num1,*num2_p=&num2;printf(“Inputthefirstnumber:”);

scanf(“%d”,num1_p);printf(“Inputthesecondnumber:”);scanf(“%d”,num2_p);printf(“num1=%d,num2=%d\n”,num1,

num2);if(*num1_p>*num2_p) exchange(num1_p,num2_p); printf(“min=%d,max=%d\n”,num1,num2);} 程序运行情况:Inputthefirstnumber:9←┘Inputthesecondnumber:6←┘num1=9,num2=6min=6,max=9思考对上面的程序作如下更改,结果如何?voidexchange(int*p1,int*p2)

{int*p;

p=p1,p1=p2,p2=p;

}再作如下变化:exchange(int*p1,int*p2){int*temp;*temp=*p1;*p1=*p2;*p2=*temp;}再作如下变化:exchange(intx,inty){inttemp;

temp=x;

x=y;

y=temp;}……exchange(num1,num2);printf(“min=%d,max=%d\n”,num1,num2);……输入3个整数,按降序(从大到小的顺序)输出。****************************************************

exchange()功能:交换2个形参指针变量所指向的变量的值形参:2个,均为指向整型数据的指针变量返回值:无****************************************************voidexchange(int*pointer1,int*pointer2){inttemp;temp=*pointer1,*pointer1=*pointer2,*pointer2=temp;}main(){intnum1,num2,num3;scanf(“%d”,&num1);scanf(“%d”,&num2);scanf(“%d”,&num3);printf(“num1=%d,num2=%d,num3=%d\n”,

num1,num2,num3);if(num1<num2)exchange(&num1,&num2);if(num1<num3)exchange(&num1,&num3);if(num2<num3)exchange(&num2,&num3);printf(“排序结果:%d,%d,%d\n”,num1,num2,num3);} 10.3数组的指针和指向数组的指针变量概述1.数组──连续存放的若干个元素的集合。2.数组名──指向此数组第一个元素的指针(首地址)。

如:inta[10],*p;p=a;或p=&a[0];3.某一元素的地址──p=&a[i]

*p等价于a[i]4.数组元素的下标在内部实现时,是按“基地址+位移”的方式处理

aa+1a+i数组元素的地址可以表示为:p+ia+i数组元素的内容可以表示为:a[i]*(p+i)*(a+i)

注意:数组名a和指向数组首地址的指针变量p不同,a不是变量。

使用下标法来引用数组元素。main(){intarray[10],i;printf(“Input10numbers:”);for(i=0;i<10;i++)scanf(“%d”,&a[i]);

printf(“array[10]:”);for(i=0;i<10;i++)printf(“%d”,a[i]); } 使用数组名来引用数组元素。main(){intarray[10],i;printf(“Input10numbers:”);for(i=0;i<10;i++)scanf(“%d”,a+i);

printf(“array[10]:”);for(i=0;i<10;i++)printf(“%d”,*(a+i)); } 使用指向数组的指针变量来引用数组元素。main(){intarray[10],*pointer=array,i;printf(“Input10numbers:”);

for(i=0;i<10;i++)scanf(“%d”,pointer+i);

printf(“array[10]:”);

for(i=0;i<10;i++)printf(“%d”,*(pointer+i));

} 程序说明:程序中第3行和第6行的2个for语句,等价于下面的程序段:for(i=0;i<10;i++,pointer++)scanf(“%d”,pointer);printf(“array[10]:”);pointer=array; for(i=0;i<10;i++,pointer++)printf(“%d”,*pointer);思考题:如果去掉“pointer=array;”行,程序运行结果会如何?改变循环条件如下:main(){inta[10],*p,i;printf(“Input10numbers:”);for(i=0;i<10;i++)scanf(“%d”,&a[i]);

printf(“array[10]:”);

for(p=a;p<(a+10);p++)printf(“%d”,*p);

}将后一个for循环作如下更改:

main(){inta[10],*p,i;printf(“Input10numbers:”);for(i=0;i<10;i++)scanf(“%d”,&a[i]);

printf(“array[10]:”);

for(p=a;a<(p+10);a++)printf(“%d”,*a);

} ×数组指针、指针变量与数组元素之间的关系设有inta[10],*p=a;则1.地址关系数组指针aa+1a+i指针变量pp+1p+i数组元素&a[0]&a[1]&a[i]2.内容关系数组指针*a*(a+1)*(a+i)指针变量*p*(p+1)*(p+i)数组元素a[0]a[1]a[i]对指向数组的指针变量进行运算

1)赋值运算

p=ap=&a[i]p=NULL注意:类型相同

2)算术运算用法:指针+/-整数如:a+ip+i

注意:a只能加不能减下标的有效范围3)两个指针的相减:求两地址的间距

p-a求它们之间有几个元素注意:两个指针类型相同,并指向同一连续存储区域4)移动指针

p++p—注意:数组名不能实施该运算。a+1a++不同5)比较运算依据地址来运算

p<q注意:2—5针对指向连续空间的指针起作用。将数组a中的数据复制到数组b中,并输出。main(){inta[5]={1,2,3,4,5},b[5];int*p=a,*q=b,i;

for(i=0;i<5;i++){*q=*p;p++;q++;}for(i=0;i<5;i++)printf(“%d”,b[i]); }作如下变化:main(){inta[5]={1,2,3,4,5},b[5];int*p=a,*q=b,i;

for(i=0;i<5;i++)*q++=*p++;

for(i=0;i<5;i++)printf(“%d”,b[i]); }printf(“%d”,*q++);q=b;数组作函数参数数组作函数参数,实参和行参关系归纳如下:将数组a中n个数按相反顺序存放。示意图如下:3791106754224576011973imjvoidinv(intx[],intn){inttemp,i,j,m=(n-1)/2;

for(i=0;i<=m;i++){j=n-1-i;

t=x[i];x[i]=x[j];x[j]=t;}return;

}数组名作形参main(){inti,a[10]={3,7,9,11,0,6,7,5,4,2};

for(i=0;i<10;i++)printf("%d",a[i]);

printf("\n");

inv(a,10);

for(i=0;i<10;i++)printf("%d",a[i]);

printf("\n");}将函数inv中的形参x改成指针变量。91106754237a[0]a[1]a[2]a[3]a[4]a[5]a[6]a[7]a[8]a[9]a数组i,xp=x+mj指针变量作形参voidinv(int*x,intn){int*p,*i,*j,m=(n-1)/2;

i=x;

j=x+n-1;

p=x+m;

for(;i<=p;i++,j--){t=*i;*i=*j;*j=t;}return;

}main(){inti,a[10]={3,7,9,11,0,6,7,5,4,2};

for(i=0;i<10;i++)printf("%d",a[i]);

printf("\n");

inv(a,10);

for(i=0;i<10;i++)printf("%d",a[i]);

printf("\n");}从10个数中找出其中最大值和最小值。intmax,min;/*全局变量*/voidmax_min_value(intarray[],intn){int*p,*array_end;

array_end=array+n;

max=min=*array;

for(p=array+1;p<array_end;p++)if(*p>max)max=*p;

elseif(*p<min)min=*p;

return;}main(){inti,number[10];

for(i=0;i<10;i++)scanf("%d",&number[i]);

max_min_value(number,10);

printf("\nmax=%d,min=%d\n",

max,min);

}

在用数组名作为函数参数时,实际上相应的形参是指针变量。

形参数组在函数执行期间,它可以再被赋值。C编译系统是将形参数组名作为指针变量来处理的。如:f(intarr[],intn){printf("%d\n",*arr);

arr=arr+3;

printf("%d\n",*arr);}2维数组的指针及其指针变量a是一个数组名。可以认为a数组是由3个一维数组组成。a[2]a[0]a[1]a915→→→102611371248a数组a+1a+2a(3A00)(3A08)(3A10)a[0]a[1]a[2]3A0013A0223A0433A0643A0853A0A63A0C73A0E83A1093A12103A24113A2612a[0]a[0]+1a[0]+2a[0]+3aa+1a+2强调二维数组名是指向行的,一维数组名是指向列的。在指向行的指针前面加一个*,就转换为指向列的指针。例如:*a,*(a+1)在指向列的指针前面加一个&,就转换为指向行的指针。例如:&a[0](&*a相当于

a)1.2维数组的指针

1.定义:inta[3][4];则

a:是一个以行为单位进行控制的行指针:

a+i:行指针值,指向2维数组的第i行。

*(a+i):(列)指针值,指向第i行第0列。

*(*(a+i)):数组元素a[i][0]的值。

用a作指针访问数组元素a[i][j]的格式:

*(*(a+i)+j)a[i]:第i行第0列元素,是一个以数组元素为单位进行控制的列指针:a[i]+j:(列)指针值,指向数组元素array[i][j]。*(a[i]+j):数组元素a[i][j]的值。如果有“inta[3][4],*p=a[0];”,则p+1指向下一个元素。用p作指针访问数组元素a[i][j]的格式:

*(p+(i*每行列数+j))用指针变量输出数组元素的值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++){if((p-a[0])%4==0)printf("\n");

printf("%4d",*p);}}输出指定元素:

计算a[i][j]在数组a(nm)中的相对位置(相对于a[0][0])的计算公式:i*m+j其中,m为二维数组的列数输出二维数组有关的值。#defineformat"%d,%d\n"main(){inta[3][4]={1,3,5,7,9,11,13,15, 17,19,21,23};printf(format,a,*a);

printf(format,a[0],*(a+0));

printf(format,&a[0],&a[0][0]);

printf(format,a[1],a+1);

printf(format,&a[1][0],*(a+1)+0);

printf(format,a[2],*(a+2));

printf(format,&a[2],a+2);

printf(format,a[1][0],*(*(a+1)+0));}运行结果如下:158,158158,158158,158166,166166,166174,174174,1749,913579111315171921232.行指针变量──指向由n个元素组成的一维数组的指针变量(1)定义格式

数据类型(*指针变量)[n];****************************************************注意:“*指针变量”外的括号不能缺,否则成了指针数组——数组的每个元素都是一个指针──指针数组。****************************************************例如:int(*p)[4];(2)赋值

行指针变量=2维数组名|行指针变量;使用行指针和列指针两种方式输出2维数组的任一元素。(1)使用行指针main(){inta[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};int(*p)[4],row,col;

p=a;printf(“Inputrow=”);scanf(“%d”,&row);printf(“Inputcol=”);scanf(“%d”,&col);printf(“array[%1d][%1d]=%d\n”,row,col,

*(*(p+row)+col));}

程序运行情况:

Inputrow=1←┘Inputcol=2←┘array[1][2]=7思考题:本题也可以直接使用数组名a作指针,应如何修改?(2)使用列指针main(){inta[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};int*p,row,col;

p=a[0];

printf(“Inputrow=”);scanf(“%d”,&row);printf(“Inputcol=”);scanf(“%d”,&col);printf(“array[%1d][%1d]=%d\n”,row,col,*(p+(row*4+col)));} 3.2维数组指针作函数参数

2维数组的指针作函数实参时,有列指针和行指针两种形式。相应的,用来接受实参数组指针的形参,必须使用相应形式的指针变量,如下所示:实参:列指针行指针↓↓形参:(列)指针变量行指针变量

有一个班,3个学生,各4门课程,计算总平均分数和查找第n个学生的成绩。

用函数average求总平均成绩,用函数search找出并输出第i个学生的成绩。main(){voidaverage(float*p,intn);

voidsearch(float(*p)[4],intn);

floatscore[3][4]={{65,67,70,60},

{80,87,90,81},{90,99,100,98}};

average(*score,12);

search(score,2);/*求第2个学生成绩*/}voidaverage(float*p,intn){float*p_end;

floatsum=0,aver;

p_end=p+n;

for(;p<p_end;p++)sum=sum+(*p);

aver=sum/n;

printf("average=%5.2f\n",aver);

}6567706080879081909910098voidsearch(float(*p)[4],intn){inti;

printf("thescoreofNo.%dare:\n",n);

for(i=0;i<4;i++)printf("%5.2f",*(*(p+n)+i));}程序运行结果如下:average=82.25thescoreofNo.2are:90.0099.00100.0098.00656770608087908190991009810.4指针和字符串字符数组与字符指针变量比较Chara[]="Ilovechina";*p=a;存储的内容不同字符数组存的是字符串,字符指针变量存的是字符串在内存的首地址。Ilovechinaa3000Hp3000H2.赋值方式不同字符数组只能对各个元素赋值,一次赋一个,要赋若干次。而字符指针变量只赋值一次,赋的是地址。如:chara[10],*p;a="china";p="hello";X3.当没赋值时字符数组名代表了一个确切的地址,而字符指针变量中的地址是不确切的。4.可以像数组那样用下标形式引用指针变量所指字符串中的字符。

char*p="abcd";putchar(p[3]);p[2]='x';5.字符指针变量的应用可以用字符指针变量指向一个格式字符串。如:char*format="%d,%d,%d";printf(format,a,b,c);1.逐个引用使用字符指针变量表示和引用字符串。main(){char*string=”IloveBeijing.”;for(;*string!=’\0’;string++)

printf(“%c”,*string);printf(“\n”);} 2.整体引用采取整体引用的办法,改写上例。main(){char*string=”IloveBeijing.”;printf(“%s\n”,string);}

程序说明:printf("%s\n",string);语句通过指向字符串的指针变量string,整体引用它所指向的字符串的原理:系统首先输出string指向的第一个字符,然后使string自动加1,使之指向下一个字符;重复上述过程,直至遇到字符串结束标志。注意:其它类型的数组,是不能用数组名来一次性输出它的全部元素的,只能逐个元素输出。例如:

intarray[10]={……};......printf("%d\n",array);这种用法是非法的

......字符串指针作函数参数用函数调用方式,实现字符串的复制。********************************************************

string_copy()函数:复制一个字符串形参:字符指针str_from接收源串,字符指针str_to存储目标串返回值:无********************************************************voidstring_copy(char*str_from,char*str_to){inti=0;for(;(*(str_to+i)=*(str_from+i))!=’\0’;i++);

循环体为空语句

}main(){chararray_str1[20]=”Iamateacher.”;chararray_str2[20];string_copy(array_str1,array_str2);printf(“array_str2=%s\n”,array_str2);}

程序说明:for(;(*(str_to+i)=*(str_from+i))!=’\0’;i++);语句的执行过程为:首先将源串中的当前字符,复制到目标串中;然后判断该字符(即赋值表达式的值)是否是结束标志。如果不是,则相对位置变量i的值增1,以便复制下一个字符;如果是结束标志,则结束循环。其特点是:先复制、后判断,循环结束前,结束标志已经复制。10.5指向函数的指针函数的指针,一个函数在编译时被分配的入口地址。例求a和b中的大者。一般方法:main(){intmax(int,int);inta,b,c;

scanf("%d,%d",&a,,&b);

c=max(a,b);

printf("a=%d,b=%d,max=%d",a,b,c);}max(intx,inty){intz;

z=x>y?x;y;returnz;}将main函数改写为:main(){int

max(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,c);}执行过程:

函数名代表该函数的入口地址。指令1指令2

……

“p=max”,是将函数max的入口地址赋给指针变量p。maxp说明:指向函数的指针变量的一般定义形式为:数据类型(*指针变量名)()函数的调用可以通过函数名调用,也可以通过函数指针调用。(*p)()不是固定指向某一函数。给函数指针变量赋值时,只需给出函数名,而不必给出参数。如:p=max;用函数指针变量调用函数时,只需将(*p)代替函数名,之后的扩弧写上实参。c=(*p)(a,b);对指向函数指的针变量,像p+n、p++、是无意义的。10.6返回指针值的函数函数可以带回指针型数据,即地址。一般定义形式为:类型名*函数名(参数表列)有若干个学生的成绩(每个学生有4门课程),要求输入学生序号后,输出该生的全部成绩。用指针函数来实现。int*search(float(*pointer)[4],intn){int*pt;

pt=*(pointer+n);

return(pt);

}main(){intscore[][4]={{60,70,80,90},{56,89,67,88},{34,78,90,66}};

int*p,i,m,;

scanf("%d",&m);

printf("thescoresofNo.%dare:\n",m);p=search(score,m);

for(i=0;i<4;i++)printf("%5d\t",*(p+i));}10.6指针数组1.概

温馨提示

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

评论

0/150

提交评论