第六章指针Word格式.docx
《第六章指针Word格式.docx》由会员分享,可在线阅读,更多相关《第六章指针Word格式.docx(17页珍藏版)》请在冰豆网上搜索。
函数max()入口地址
通常我们关心的不是具体的地址值,而是每个实体的“起始地址”。
实体地址表示法1:
·
普通变量——&
a,&
b,&
数组d[3]——d(数组名)&
d[0],&
d[1],&
对二维数组,可用单下标法表示每行首地址。
如对charx[3][4],可用x[0]、x[1]、x[2]分别表示每一、二、三行的首地址。
函数max()——max(函数名)入口地址
取地址运算符(适用于普通变量或数组元素)
实体地址表示法2:
指针
适合于地址运算(加减等)
【说明】字符串是一种特殊的实体,存放在内存用户区的常量区(参见第四章第五节)。
2.指针(pointer)
实体地址的一种表示法(便于编程处理)。
指针是一种特殊的数据类型——存放的是某个实体的地址值。
为什么要用指针?
直接访问:
通过变量(实体)名→定义时分配的地址→变量值访问变量
好比“先坐再买票”看电影,来一个观众,分配一个空位给他去坐,并且还要在纸上记一个某人坐在哪里。
这种方式对用户来说很方便(“直接就座”),但对系统来说,“找某人”就极不方便(间接:
查名字→座号)
间接访问:
通过“指针”访问变量
好比先买票(票上座号——指针),再按号入座看电影。
这种方式对用户来说属于“间接就座”,便对系统查找来说就很直接,且便于处理。
尤其对于数组(团体),可通过指针简单自加或自减,对整个数组进行处理。
【变量的“指针”】变量的地址
3.指针变量P159
存放“指针”(地址值)的特殊变量。
定义方法:
类型标识符*变量名
如int*a;
char*b;
float*c;
此处,指针变量a,b,c分别指向某个未确定的整型变量、字符变量和实型变量。
但指针变量a,b,c本身是整数(地址)。
(编程测试可以发现在PC机上指针变量取值范围为-32767~32767,超出该范围编译就会出错,以此推算PC机地址长度为16位即2个字节)。
以下程序亦可证实这一点:
main()
{char*a;
longint*b;
double*c;
printf("
%d,%d,%d\n"
sizeof(a),sizeof(b),sizeof(c));
}
结果:
2,2,2
【注意】数组名是常量,不能自加、自减或重新赋值,指针变量则可以。
如chara[10],*b,x=5;
a++;
或a=100;
a=x;
×
(编译出错)
b++;
或b=100;
b=x;
√(编译不出错)
4.指针变量的运算与引用P159
a.与指针有关的运算符
&
取地址运算符(变量名前加一个&
,表示该变量的地址)
*指针运算符(“间接访问”运算符)
*放在某个指针变量前,表示该指针变量所指向的变量。
(注意与乘号“*”的区别)
简单记忆:
如果p被定义成指向普通变量的指针变量,则*p代表该变量的值。
如int*p,a=5;
p=&
a;
则*p代表变量a的值(5)。
·
如果p被定义成指向某个数组的指针变量,则*p代表该数组中某个元素的值。
如int*p,a[3]={1,2,3};
p=a;
则*p代表数组a中某个元素的值。
【例】main()
{int*p,a[12]={1,2,3,4,5};
clrscr();
for(;
*p<
5;
p++)
%d"
*p);
结果:
1234
如果p被定义成指向某个字符数组或某个字符串的指针变量,则*p代表某个字符。
如int*p,a[3]=”abcd”;
p代表a中的某个字符
【例】
{char*p,a[12]="
abcde"
;
*p;
%c"
}
abcde
{char*p;
p="
与*组合使用时:
若inta,*p;
p=&
则&
*p=&
a=p;
*&
a=a=*p
开头为地址,*形状为变量值,&
和*可以看作互相“抵消”。
【例一】main()
{inta=5,*p;
printf(“%d,%d,%d\n”,p,*p,a)
}
结果(按右图):
2000,5,5
【例二】以下程序的运行结果是什么?
{inta,b=10,*p;
b;
a=*p+3;
a=%d,b=%d\n"
a,b);
a=13,b=10
【例三】main()
{floatx,y;
int*p;
x=3.14;
x;
y=*p;
y=%f\n"
y);
y=-2621.000000(把int*p改为float*p后,结果正确y=3.140000)
b.指针变量的算术运算
只有加、减两种p+5p++p-1p--
注意加减运算是以实体为单位而不是以字节为单位。
此外,两个指针变量可以相减。
即:
如果两个指针变量指向同一数组时,两个指针变量值之差是两个指针之间的元素个数。
P213
但两个指针变量相加并无实际意义。
c.指针的逻辑比较P213
指针变量指向同一个对象(如数组)的不同单元地址时,才可以进行比较。
地址在前者为小。
实例:
《试题汇编》【9.56】(此处不讲解程序)
p=NULL表示指针变量为空值(不指向任何变量)。
任何指针变量或地址都可以与NULL作相等或不相等的比较。
如if(p==NULL)……
【注意】在指针p指向某个实体的地址之前,不可对*p进行赋值。
否则可能发生意想不到的错误(p随便指向某个单元)。
典型错误:
1、指针变量定义后,未指向具体存储单元就使用(此时指针变量所指单元是任意的)
2、指针变量定义后,虽指向具体存储单元但未赋值就参加运算(此时其值是任意的)
【例一】分析以下句子的错误
int*p,*q;
q=p;
p指向?
(错误类型1)
inta=20,*p,*q=&
*p=*q;
inta,*p,*q;
q=&
*q=?
(错误类型2)
【例二】若有定义char*p,ch;
则不能正确赋值的语句组是。
(01-1-06考题)
A)p=&
chB)p=(char*)malloc
(1);
Scanf(“%c”,p);
*p=getchar();
C)*p=getchar();
D)p=&
ch;
P=&
*p=getchar();
答案:
C(指针p未指向任何变量就赋值)
【例三】如果有定义语句char*a,b[30];
试判断以下各句哪些是正确的?
A)a=”abcde”;
B)b=”abcde”;
C)scanf(“%s”,a);
×
D)scanf(“%s”,b);
【例四】若有说明:
int*p,m=5,n;
试判断以下各句哪些是正确的?
《试题汇编》【9.13】
A)p=&
n;
scanf(“%d”,&
p);
B)p=&
scanf(“%d”,*p);
C)scanf(“%d”,&
n);
*p=n;
D)p=&
*p=m;
D
【例五】若有说明:
int*p1,*p2,m=5,n;
A)p1=&
m;
p2=&
p1;
(p2应为**p2)
B)p1=&
p2=&
*p1=*p2;
(*p2=?
)
C)p1=&
*p2=*p1;
(p2指向?
D)p1=&
p2=p1;
5.多重指针
形式如int**p等效于int*(*p)
——定义一个二级指针(指向指针的指针)存放某个指针变量的地址
在引用时,*p是p间接指向的对象的地址。
**p是p间接指向的对象的值。
【例一】以下程序段的输出是什么?
int**pp,*p,a=20,b=30;
pp=&
p;
p=&
printf(“%d,%d\n”,*p,**pp);
30,30
6.指针变量作为函数参数
【例一】P163例9.3
#include<
stdio.h>
fun(int*i)
{staticinta=1;
*i+=a++;
{intk=0;
fun(&
k);
printf("
%d\n"
k);
第一次调用fun(&
k)后,k=*i=1,a=2;
第二次调用fun(&
k)后,k=*i=3,a=3;
3
【讨论】如果fun()函数中没有static,结果呢?
(2)
为了实现:
在被调函数中改变实体值,然后在主调函数中使用这些改变了的实体值
主要技术要点在于:
主调函数的实参和被调函数对应的形参都必须用地址表示——地址传递
用于作实参的地址可以是:
变量名&
数组元素名(很少使用)
数组名
指针变量名
对应的形参则为:
数组定义
指针定义
二、指针与数组
1.C语言的有关规定
对于数组inta[10];
C语言规定:
①可以用数组名a代表数组首地址(即数组第一个元素a[0]的地址)
即a=&
a[0]
【注意】数组名a是个常量,只能表示该数组的首地址,不可重新赋值。
②可以用指针变量p代表数组地址或数组元素的地址。
inta[10],*p;
p=a;
或p=&
a[0];
【注意】p是变量,并不固定表示首地址,p=a只是特例,可以重新赋值。
③可以用a+i或p+i表示数组元素a[i]的地址。
如:
a+5即&
a[5]
p+5即&
a[5]
本质上,a+i或p+i相当于a+i×
d或p+i×
d
d—每个数组元素占的字节数
【讨论】如果p=&
a[2],那么p+1和p+2指向何处?
④可以用*(p+i)或*(a+i)表示数组元素a[i]。
⑤可以用p[i]表示*(p+i)即a[i]。
注意事项:
①注意指针变量当前值P169例9.6
②注意不要超界
③常见表示法:
*p++等价于*(p++)先取*p值,然后p++。
*(++p)p先自加,然后取*p的值。
*p--与*(--p)功能同上。
2、数组、指针与函数调用
由上可知:
数组名a——常量,恒为数组第一元素地址(a=&
a[0])
指针变量p——变量,可为数组任一元素地址(p=&
a[i],仅当p=a时,p=&
a[0])。
所以,若在主调函数中有一数组,需通过被调函数改变其元素值,可有四种地址传送方式可用;
P175
主调函数中实参被调函数中形参
数组名a数组名b
指针变量p(p=a)指针变量x
数组名a指针变量x
指针变量p(p=a)数组名b
其本质都是将数组名a或指针变量p所代表的数组首地址,传给形参首地址b或x
3、二维数组问题
①排队表示法(一维方法)
以a或a[0]为“起点”,顺序排列,直到“终点”(成一队列,不考虑行列)。
a[0]a[0]+i
如果inta[N][M],*p;
或p=a[0];
则数组元素a[i][j]的存储地址为:
a[0][0]+i×
N+j或a[0]+i×
N+j
且有a[0]+11,p+11从a[0]开始第11号元素的地址
*(a[0]+11),*(p+11)从a[0]开始第11号元素的值
此时可用单循环遍历整个数组各个元素:
for(i=0;
i<
N*M;
i++,p++)或for(p=a[0];
p<
a[0]+N*M;
②行列表示法(二维方法)
以a=&
a[0][0]为“原点”(按行列分)。
此时数组元素a[i][j]的地址可以表示为:
a[i][j]
a[i]+j
*(a+i)+j
而数组元素a[i][j]的值可以表示为:
*(a[i]+j)
*(*(a+i)+j)
此时,欲遍历该数组的各个元素,必须用双重循环:
N;
i++)
for(j=0;
j<
M;
j++)
4、行指针
形式:
int(*p)[n]
含义:
p为指向含有n个元素的一维数组的指针变量。
P212
使用:
二维数组可以视为由若干一维数组组成。
若inta[4][5];
int(*p)[5];
p=a[0];
则(*p)[0]=a[0][0];
(*p)[1]=a[0][1];
(*p)[2]=a[0][2];
……
(*(p+1))[0]=a[1][0];
(*(p+1))[1]=a[1][1];
(*(p+i))[j]=a[i][j];
【例】若有以下定义和语句,且0≤i<4,0≤j<3,则不能访问a数组元素的是。
inti,(*p)[3],a[][3]={1,2,3,4,5,6,7,8,9,10,11,12};
A)*(*(a+i)+j)B)p[i][j]
C)(*(p+i))[j]D)p[j]+j
D(p[j]+j是个地址)2002-1-6考试第27题
5、指针与字符串
【例一】求以下程序的运行结果。
main(){
char*pc="
#Fujian##Province#"
while(*pc)
{while(*pc=='
#'
)pc++;
if(*pc=='
\0'
)break;
*pc);
pc++;
\n"
);
FujianProvince
【例二】把字符串t复制到字符串s中。
①数组方案
chars[15],t[]="
Iamastudent."
inti=0;
while((s[i]=t[i])!
='
i++;
%s\n%s"
s,t);
②指针方案
char*t="
*s="
"
char*t1=t,*s1=s;
while((*s=*t)!
{s++;
t++;
s1,t1);
Iamastudent.
Iamastudent.
【讨论】指针方案中为什么要使用char*t1=t,*s1=s;
语句?
(s++;
的结果是两指针都指向字符串的未尾’\0’处。
所以如果输入s和t将无字符输出)
∴使用指针时要注意当前指针的位置!
【例三】以下函数的功能对应于。
intfun(char*s,char*t)
{while(*s)&
(*t)&
(*t==*s)}/*在s和t均未结束且相比较的字符相同时继续循环*/
{t++;
/*将t下移一个字符*/
s++;
/*将s下移一个字符*/
return(*s-*t);
/*如果两个字符相同,返回值为0,否则不为0*/
A)strlen(s)+strlen(t)B)strcmp(s,t)
C)strcpy(s,t)D)strcat(s,t)
B
技巧:
用*s作循环条件,当循环到字符串结束处时自动结束循环)
while(*s)
{…;
s++};
6、指针数组
定义:
char*p[5];
功能:
定义数组p[5],其每个元素p[0]、p[1]、…p[4]都是指针变量。
通常用于指向一组字符串。
此时,对于p[i],其下标表示第i个字符串,p[i]本身是第i个字符串的首地址。
与行指针的区别:
行指针——只能指向二维数组的某一行(靠p+i来换行)。
其元素是某一行中对应字符。
指针数组——其元素分别向二维数组的首地址。
典型应用:
带参数的主函数P210
【例一】已知TEST.C的源程序如下:
(此部分内容可参见教材P210~211)
main(intargc,char*argv[])
{while(argc>
1)printf("
%s"
argv[--argc]);
将该文件编译后,在命令行输入:
testabc123则该程序运行结果是什么?
123abc
【分析】执行时,argc的值为3(从命令行输入testabc123共计三项)
argv[0]=”test”argv[1]=”abc”argv[2]=”123”
【例二】以下程序经过编译连接后得到的可执行文件名为echo.exe,在dos提示符下输入,则在屏幕上将显示Mycomputer。
argv[0]
echo
argv[1]
My
argv[2]
computer
argc=3
main(intargc,char*argv[])
{inti;
for(i=1;
argc;
%s%c"
argv[i],(i<
argc-1)?
'
'
:
\n'
A)MycomputerB)echoMycomputer
C)MycomputerD)main(Mycomputer)
【分析】执行时,argc的值为3(从命令行输入echoMycomputer共计三项)
argv[0]=”echo”argv[1]=”My”argv[2]=”computer”
三、指针与函数
1、指向函数的指针变量P196
可以用指针变量指向一个函数。
一个函数在编译时被分配给一个“入口地址”。
定义方法:
类型标识符(*指针变量名)();
如int(*p)();
指向一个返回整型值的函数
用法:
设有函数fun(a,b)
令p=fun;
则有(*p)(a,b)相当于fun(a,b);
此时c=(*p)(a,b)与c=fun(a,b)等效。
2、返回指针值的函数:
一般形式int*a(x,y);
表示该函数的返回值是一个指针。