第 10章指针 (4学时 )
指针是一种数据类型,是指存放数据的内存地址
指针变量是一种变量,该变量存放的数据就是指针型的数据,即内存地址
程序中可以通过变量名或数组名 [下标 ]来引用变量或数组元素,也可以通过指针变量来引用变量或数组元素
指针变量的引用方式运行速度更快
使用指针变量前需将指针变量
“指向”变量或数组 (元素 )
“指向”就是将变量或数组 (元素 )
的地址存放到指针变量中指针变量的定义和引用
指针就是地址,指针变量就是存放地址型数据的变量指针变量的定义
格式:存储类型 数据类型 *指针变量名 1
[=初值1 ],……
其中,存储类型,[auto],static
数据类型:任何基本类型初值:变量、数组元素的地址或数组的首地址
注意:
1,在一个定义语句中,可以同时定义普通变量、数组及指针变量,并赋初值
2.,数据类型”是将要指向的变量或数组的数据类型
3,指针变量前必须有,*”号
4,初值是,&普通变量,&数据元素或数组名
例:指针变量的定义和初始化
int a,*p1=&a;
char s[10],*p2=s;
float x[5],*p3=&x[2];
p3
p1 &a
a
p2 s
S数组
&x[2] X[0]
X[1]
X[2]
X[3]
X[4]
指针变量的引用
程序中引用指针变量常用的方式有3种
1,给指针变量赋值形式:指针变量=地址型表达式作用:指针变量指向目标变量
例:给指针变量赋值
int i,a[10],*p_i;
p_i=&i; p_i=a+6;
p_i=a; 注,a+6是& a[6]
p_i=&a[2]; a+6 a+6*2(字节 )
2,直接使用指针变量名
需要用到地址时,可以直接引用指针变量名
例:直接使用指针变量名
float y[10],*p1=y,*p2;
p2=p1;
scanf(“%f,%f”,p1,p2+6);/* 存入 y[0],y[6]*/
3,通过指针变量来引用它所指向的变量
形式:*指针变量名
例,利用,*指针变量”引用所指向的数据
float x1=12.6,x2=2.0,x3,*p1=&x1,*p3=&x3;
char s[10]=“abcd”,*ps1=s,*ps2=s+4;
*p3=*p1*x2;
*ps2=*ps1+2; /* 地址运算 */
解释:
*p3=*p1*x2; x3=x1*x2 x3=12.6× 2.0
*ps2=*ps1+2; s[4]=s+2 s[4]=s[2]
s[4]=?c?
取地址运算符和指针运算符
,&” 称为取地址运算符,运算 结果 是地址
,*,称为指针运算符,运算 对象 是地址取地址运算符和指针运算符名称 对象数与位置 运算符 对象类型 运算规则 结果类型 结合性取地址单目前缀
& 任何类型的变量、数组元素取运算对象的地址地址指针单目前缀
* 地址 地址对应 的变量或数组元素变量或数组元素的类型自右向左
取地址运算符“&”的运算对象必须是已定义过的变量或数组元素
例,取地址运算符的使用
int a,b[10];
&a,&a[0],&b[9] 正确
指针运算符“*”的运算对象必须是地址
常用已赋值的指针变量,也可以是普通变量的地址或数组元素的地址
运算结果是地址所对应的变量或数组元素等
例:指针运算的使用
int a,b[10],*p1=&a,*p2=&b[2],*p3=b;
*p1 a (*的运算对象是 a的地址 )
*p2 b[2]
*(p3+6) b[6]
*(&a) a
*(&b[3]) b[3]
*b b[0]
在混合使用&和*时,要注意分清它们各自的运算对象和运算时的结合性
例:混合使用&和*两种运算符
int a,*p=&a; 结果
*&a 相当于 *(&a) a
&*a 错,相当于 &(*a) *a
*&p 相当于 *(&p) p
&*p 相当于 &(*p) &a
指针变量的应用
指针变量的应用主要是用来表示它所指向的数据对象
包括普通变量、一维数组元素、二维数组元素和字符串等数据对象
指向变量的指针变量的应用使用指向普通变量的指针变量主要包括:
利用赋值方式使得指针变量指向变量,以后利用,*指针变量”来引用所指向的普通变量使用指向普通变量的指针变量要注意几点
① 数据类型 (是目标变量的类型)
例:
int a,*pa;
float x,*px;
pa=&a;
px=&x;
pa=&x; 错,类型不一致
②指针变量必须先赋值后引用(有指向)
技巧:若 p指向 a,则 p是 a的地址,*p就是 a
例:指针变量没赋值其结果不可预料错!!!
int a=5,*p1=&a,*p2;
*p1=*p1+*p2; p2指向的地址是随机的
*p2=*p1;,.
例:输入 3个实数,找出其中的最大数
1.main( )
2.{float x1,x2,x3,*p1,*p2,*p3,*pmax;
3,p1=&x1,p2=&x2,p3=&x3;
4,scanf("%f,%f,%f",p1,p2,p3);
5.pmax=p1;
6.if(*pmax<*p2)
7,pmax=p2;
8.if(*pmax<*p3)
9,pmax=p3;
10.printf("MAX=%f\n",*pmax);
11.}
指向一维数组的指针变量的应用
两种方式:
1,指针变量指向数组元素
2,指针变量指向数组首地址
回顾一维数组的地址
设 int a[10];
数组元素 a[i]的地址,&a[i] 或 a+i
数组 a的首地址,a 或 &a[0]
指向一维数组指针变量的应用
当指针变量指向下标为 i的一维数组元素时,
引用数组元素的方法为:
引用,数组元素 [i]” *(指针变量 +0) 或 *指针变量引用,数组元素 [i-k]” *(指针变量 -k)
引用,数组元素 [i+k]” *(指针变量 +k)
例:使用指向一维数组元素的指针变量处理数组元素
1,main( )
2,{char s[ ]={'Z','L','H','a'},*p1,*p2;
3,p2=p1=&s[2];
4,if(*p2<*(p1-2)) p2=p1-2;
5,if(*p2<*(p1-1)) p2=p1-1;
6,if(*p2<*(p1+1)) p2=p1+1;
7,printf("%c\n",*p2);
8,}
9,结果,a
10,功能:找出字符数组中的最大元素.
指向一维数组首地址的指针变量的应用
当指针变量指向为一维数组首地址时,引用,数组元素 [i]”的方法为:
1,*(指针变量 +i)
2,*(数组名 +i)
3,指针变量 [i]
4,数组名 [i]
注,i=0时,有,*指针变量,*数组名
数组名是常量,与指针变量不同指针法下标法
提示:用指针变量处理一维数组的关键是搞清楚数组元素的地址
例:从键盘上输入10个字符,从中找出最大的字符
1,main( )
2,{char s[10],*p=s,*pmax=s;
3,int i;
4,for(i=0;i<10;i++)
5,scanf(“%c”,p+i);
6,for(i=0;i<10;i++)
7,if(*pmax<*(p+i))
8,pmax=p+i;
9,printf(“%c\n”,*pmax);}
指向一维数组的指针变量的运算
1,指向一维数组的指针变量算术运算
指针变量 ± 整数
++(-- )指针变量
指针变量 ++(-- )
例:
int a[10],*p=a; /* 假定分给 a的地址是 2000*/
p=p+5; /* 地址为 2000+5× 2*/
p--;
a=a+2;?
a++;?
数组名为常量!!!
2,指向同一数组的指针变量之间的减法运算
指针变量1-指针变量2
结果为,两个指针之间的数组元素个数
例:指向同一数组的指针变量相减
float x[10],*p1=x+2,*p2=&x[8];
p1-p2 -6
p2-p1 6
相隔6个元素
3,指向一维数组的指针变量的关系运算
关系运算的规则:
指针变量1 关系运算符 指针变量2
例:地址数据之间的关系运算
char s[ ]=“123456789”,*p1=s+3,*p2=&s[8];
p1>=p2 0 假
p1==&s[3] 1真
--p2!=p1+4 0 假
p1<s 0 假
例:重编10个字符,从中找出最大的字符
1,main( )
2,{char s[10],*p,*pmax=s;
3,int i;
4,for(p=s;p<s+10;p++)
5,scanf(“%c”,p);
6,for(p=s;p<s+10;p++)
7,if(*pmax<*p)
8,pmax=p;
9,printf(“%c\n”,*pmax);}
提示:用指向一维数组首地址的指针变量依次处理一维数组元素的标准程序段有两种,(p19,p23)
1,for(i=0;i<数组长度; i++)
{处理当前的数组元素,*(指针变量 +i)}
2,for(指针变量 =数组首地址;指针变量 <数组首地址 +数组长度;指针变量 ++)
{处理当前的数组元素,*指针变量 }
4,关于一维数组元素的下标越界问题
允许下标越界,但结果难以预测
例:数组元素的下标越界问题
int a[10],*p=a;
*(p-1)=*(a+10)=1;或 a[-1]=p[10]=1;
解释:语法无错,数据存入两个未知单元
例:将数组 a中 n个整数按相反顺序存放
1,void inv(int x[ ],int n)
2,{
3,int temp,i,j,m=(n-1)/2;
4,for(i=0;i<=m;i++)
5,{j=n-1-i;
6,temp=x[i];x[i]=x[j];x[j]=temp;}
7,return;
8,}
3 7 9 1 0 6 7 5 4 2
2 4 5 7 6 0 1 9 7 3
i m j
1,main( )
2,{int i,a[10]={3,7,9,1,0,6,7,5,4,2};
3,printf("The original array:\n");
4,for(i=0;i<10;i++)
5,printf("%d",a[i]);
6,printf("\n");
7,inv(a,10);
8,printf("The array has been inverted:\n");
9,for(i=0;i<10;i++)
10,printf("%d",a[i]);
11,printf("\n");
12.}
形参 x为指针变量
1,void inv(int *x,int n)
2,{
3,int *p,temp,*i,*j,m=(n-1)/2;
4,i=x;j=x+n-1;p=z+m;
5,for(;i<=p;i++,j--)
6,{temp=*i;*i=*j;*j=temp;}
7,return ;
8,}
3
7
9
1
0
6
7
5
4
2
A[0]
A[1]
A[2]
A[3]
A[4]
A[5]
A[6]
A[7]
A[8]
A[9]
A数组i,x
p=x+m
j
例 10.8从 10个数中找出其中最大值和最小值
1,int max,min; /* 函数只能返回一个值 */
2,void max_min_value(int array[ ],int n)
3,{int *p,*array_end;
4,array_end=array+n;
5,max=min=*array;
6,for(p=array+1;p<array_end;p++)
7,if(*p>max)max=*p;
8,else if(*p<min)min=*p;
9,return;}
1,main( )
2,{int i,number[10];
3,print("enter 10 integer numbers:\n");
4,for(i=0;i<10;i++)
5,scanf("%d",&number[i]);
6,max_min_value(number,10);
7,printf("\nmax=%d,min=%d\n",max,min);
8,}
例 10.10用选择法对 10个整数排序
sort(int *x,int n)
{int i,j,k,t;
for(i=0;i<n-1;i++)
{k=i;
for(j=i+1;j<n;j++)
if(*(x+j)>*(x+k))k=j;
if(k!=i)
{t=*(x+i);*(x+i)=*(x+k);*(x+k)=t;}
}}
1,main( )
2,{int *p,i,a[10];
3,p=a;
4,for(i=0;i<10;i++)
5,scanf("%d",p++);
6,p=a;
7,sort(p,10);
8,for(p=a,i=0;i<10;i++)
9,{printf("%d ",*p);p++}
10.}
指向二维数组的指针变量的应用
分为三种形式:
1,指针变量指向二维数组元素
2,指针变量指向二维数组首地址
3,指针变量指向二维数组中的某个一维数组
回顾二维数组地址 int x[10][10];
1,首地址,x,x[0],&x[0][0]
2,一维数组 (i行 )首地址,x[i],x+i
3,元素 x[i][j]的地址,&x[i][j],x[i]+j
应用方式,*指针变量
例,输入 3× 4二维整型数组元素,然后按照行列的格式输出
1,main( )
2,{int x[3][4],*p,i,j;
3,for(i=0;i<3;i++)
4,for(j=0;j<4;j++)
5,{p=&x[i][j];
6,scanf("%d",p); }
7,for(i=0;i<3;i++)
8,{for(j=0;j<4;j++)
9,{p=x[i]+j;
10,printf("%8d",*p); }
11,printf("\n");}}
例 10.12用指针变量输出数组元素的值
1,main( )
2,{int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};
3,int *p;
4,for(p=a[0];p<a[0]+12;p++)
5,{if((p-a[0])%4==0) printf(“\n”);
6,printf(“%4d”,*p);
7,}}
运行结果,1 3 5 7
9 11 13 15
17 19 21 23
指向二维数组首地址的指针变量的应用
当指针变量指向二维数组首地址时,引用 i
行 j列的数组元素的方法如下:
*(指针变量 +i*列长度 +j) 指针法或 指针变量 [i][j] 下标法注意:指针变量 +i*列长度 +j代表了第 i行、第 j
列元素的地址;但是不能用 *(数组名 +i*列长度 +j)的方式来引用第 i行、第 j列的元素
例:输入 3× 4二维整型数组元素,然后按照行列的格式输出 (重编 )
1,main( )
2,{int x[3][4],*p,i,j;
3,for(i=0;i<3;i++)
4,for(j=0;j<4;j++)
5,scanf(“%d”,p+i*4+j);
6,for(i=0;i<3;i++)
7,{for(j=0;j<4;j++)
8,printf("%8d",*(p+i*4+j)); }
9,printf("\n");}}
指向二维数组中一维数组的指针变量的应用
1,定义指向二维数组中一维数组的指针变量定义方法:
存储类型符 数据类型符 (* 指针变量 )[m],…
其中,m对应二维数组的列长度
2.让指向二维数组中一维数组的指针变量指向二维数组首地址
int (*指针变量 )[m]=二维数组名 (赋初值)
指针变量 =二维数组名 (赋值)
3 使用指向二维数组中一维数组的指针变量处理二维数组元素
设,int a[3][4],(*ip)[8]=a;
第 i行元素组成的一维 数组首地址:
*(指针变量 +i)、指针变量 +i
如,*(ip+i) ip+i ip[i]
第 i行 j列元素的地址:
*(指针变量 +i)+j
如,*(ip+i)+j ip[i]+j
第 i行 j列元素:
*(*(指针变量 +i)+j)
如,*(*(ip+i)+j) ip[i][j]
例:利用指向二维数组中一维数组的指针变量处理二维数组
int x[6][8],(*p)[8]=x;
*(p+2) 表示 第二行元素组成的一维数组首地址
p+2 第二行元素组成的一维数组首地址
*(p+2)+3 数组元素 x[2][p3]的地址
(p+2)+3 第 5行元素组成的一维数组首地址
*(*(p+2)+3) 数组元素 x[2][p3]
*((p+2)+3) 第 5行元素组成的一维数组首地址
例:使用指向二维数组中一维数组的指针变量处理二维数组元素.重编输入 3× 4…
1,main( )
2,{int x[3][4],(*p)[4],i,j;
3,for(i=0;i<3;i++)
4,for(j=0;j<4;j++)
5,scanf("%d",*(p+i)+j);
6,for(i=0;i<3;i++)
7,{for(j=0;j<4;j++)
8,printf("%8d",*(*(p+i+j));
9,printf("\n");
10,}
11.}
二维数组中的地址问题的再讨论
二维数组首地址:
数组名、数组名 [0],&数组名 [0][0]
第 i行元素组成的一维数组首地址:
数组名 [i]、数组名 +i,*(数组名 +i)
第 i行 j列数组元素的地址:
数组名 [i]+j,*(数组名 +i)+j)
第 i行 j列数组元素:
数组名 [i][j],*( *(数组名 +i)+j),*(数组名 [i]+j)
第 0行 0列数组元素:
*( *(数组名 +0)+0) **数组名指向字符串的指针变量的应用
当字符型数组中存放的是字符串时,
可使用指向其首地址的指针变量来整体处理其中的字符串
1,如何将指针变量指向字符串常量方法一 char *指针变量 =字符串常量 ;
方法二 字符型指针变量 =字符串常量 ;
2,指向字符串常量的指针变量的使用
输出字符串:
printf(“%s”,指针变量 ),puts(指针变量 )
输入字符串:
scanf(“%s”,指针变量 ),gets(指针变量 )
引用字符串的第 i个字符:
*(指针变量 +i)
例:让指针变量指向字符串常量
char s[10],*p1,*p2=“1234”;
p1=p2;
p2=“teacher”;
printf(“%s\n”,p2); 输出,teacher
printf(“%s\n”,p1); 输出,1234
p1=“student”;
printf(“%s\n”,p1); 输出,student
s=“computer”; 问?
答:错!
3,指向存放字符串的字符数组的指针变量的使用例:用指针变量处理字符串常量和存放在字符数组中的字符串.
1,#include "string.h"
2,main( )
3,{char *p="ABCDEF",s[10],*p1=s,*pp;
4,int flag;
5,pp=p;
6,scanf("%s",p1);
7,flag=strcmp(p1,p);
8,if(flag>0) printf("%s > %s \n",s,pp);
9,else if(flag==0) printf("%s == %s \n",s,pp);
10,else printf("%s < %s \n",s,pp);}
指针数组和多级指针