本课重要知识点:
1.知识点:指针的概念与指针变量
l 指针就是地址。指针变量是用来存储地址,而一般变量是用来存储数值。
例1.(09-09-26)有如下程序输出结果为 1,2,2,1
#include <stdio.h>
main()
{ int m=1,n=2,*p=&m,*q=&n,*r;
r=p ;p=q ;q=r ;
printf(“%d,%d,%d,%d\n”,m,n,*p,*q) ;
}
【解析】p、q为指针,初始化时p指向m,q指向n。执行r=p ;p=q ;q=r ;p和q的值交换,从而p指向n,q指向m。指针的改变不会应用m、n的值,最后输出*p和*q的值分别为n、m的值。
2.知识点:指针的引用
l 指针的引用是通过两个运算符实现“&”和“*”实现的
&是取地址符号,它的一半格式是:&变量名。例如:int i ; &i表示取i的地址。
*是取值运算符,它的一般格式:*指针变量名。例如:inti=10,*p=&i; *p表示取i的值10
例1.(2011-09-25)若定义语句:intyear=2009,*p=&year;,以下不能使变量year中的值增至2010的语句是 (D)
A.*p+=1; B.(*p)++; C.++(*p); D.*p++;
【解析】*和++同级别,按自左向右的结合方向,因此D选项可转变为*(p++),从而只是使指针发生移动,而不能将p所指变量增1
例2.(06—04—24)若有说明语句:double *p,a;则能通过scanf语句正确给输入项读入数据的程序段是 (D)
A)*p=&a; scanf("%lf",p);
B)*p=&a; scanf("%f",p);
C)p=&a; scanf("%lf",*p);
D)p=&a; scanf("%lf",p);
【解析】对于scanf函数,输入数据列表必须是合法地址表达式(可以使地
址常量、指针),A选项、B选项*使用错误。
例3.(09-03-16)若有定义语句 doule x,y,*px,*py;执行了px=&x;py=&y;之后,正确的输入语句是(C)
A) scanf("%f%f",x,y); B) scanf("%f%f"&x,&y);
C)scanf("%f%le",px,py); D) scanf("%lf%lf",x,y);
【解析】A,D选项中的x,y缺少取地址符,B项中&x之前缺少逗号,格式不正确。
3.知识点:指针变量的初始化
l 指针变量在使用前必须要初始化,把一个具体的地址赋给它,否则引用时会出错,如果不指向任何数据就赋“空值”NULL。
l 指针变量两种初始化方法:方法一:int a=2,*p=&a;(定义的同时初始化)
方法二:int a=2,*p;p=&a;(先定义后初始化)
例1.(07—04—29)设已有定义:float x;则以下对指针变量P进行定义且赋初值的语句中正确的是 (D)
A)float *p=1034; B)int *p=(float)x; C)float p=&x; D)float *p=&x;
【解析】可以给一个指针赋值的只能是一个与该指针同类型的指针(或地址值);故A、B选项错误;C选项声明指针出错,P前面的指针说明符*不能省略。
4.知识点:指针的运算
l *p++和(*p)++之间的差别:*p++是地址变化,(*p)++是指针变量所指的数据变化。一个指针变量加一个整数不是简单的数学相加,而是连续移动若干地址。当两个指针指向同一数组时,它们可以比较大小进行减法运算。
例如:int a[10],*p; p=a; p++;表示p移动一个存储单元,p指向a[1],只有当指着指向数组元素时,指针的运动才有意义。
5:知识点:指针与数组
l 指针与一维数组
数组名代表数组的首地址。一维数组中,第一个元素的地址即为该数组的起始地址。建立指针变量与一维数组的联系:
例如: inta[6],*pa; pa=a;或pa=&a[0];
说明:①数组名a代表该数组的首地址,也即a[0]的地址。
② pa=a;或pa=&a[0];使pa保存了数组a的首地址,pa,a,&a[0]都指向一个地址。
③以上操作可等价于 int a[6],*pa=a;
④如果pa=&a[3];表示*pa的值就是a[3]的地址。
注意:数组a是一个地址常量,它永远指向数组的首地址,不能重新赋值。因此 a=&i;或a++都是错误的。
例1.(08—04—23)有以下程序
#include<stdio.h>
main()
{int a[]={1,2,3,4),y,*p=&a[3];
--p;y=*p;printf("y=%d\n",y);
} 程序的运行结果是(D)
A)y=0 B)y=l C)y=2 D)y=3
【解析】程序中定义一个一维数组,并初始化,再一定义了一变量和一个指针(将指针指向数组下标为3的一个元素),执行--p;(注意,当指针指一个元素后,指针变量加上“*”时,表示引用的是元素的值,当不加“*“时表示引用的是地址),向前移动一个位置,指向a[2],y=+p;(将p指针指向的值3赋给变量y),输出y的值为3。
例2.(11-03-24)设有定义:double x[10],*p=x;,以下能给数组x下标为6的元素读入数据的正确语句是( C )
A)scanf("%f",&x[6]); B)scanf("%lf",*(x+6));
C)scanf("%lf",p+6); D)scanf("%lf",p[6]);
【解析】数组名是数组的首地址,p=x,指针p指向数组的首地址,要表示数组x下标为6的元素的地址可以有&x[6],x+6,p+6,&p[6],scanf后面的参数列表必须是地址列表,B中*(x+6)D中p[6]都是取数组x下标为6的元素的值,A中格式控制符%f与double类型不匹配,所以选C。
6.知识点:用指针访问数组元素
l 通过指针引用数组元素
例如:int*p,a[5]; p=&a[0];
说明:①指针变量p指向了数组元素a[0],可以使用访问运算符“*”来引用变量a[0];
例:*p=18;等价于a[0]=18;
②*(p+1)表示a[1] p+1表示&a[1]
l 通过数组的首地址引用数组元素
例如:int a[5];
说明:①a是数组名,表示收地址,可以把a当做一个指针常量。
②*a等价与a[0],*(a+1)等价与a[1];a等价于&a[0],a+1等价与&a[1];
l 用带下标的指针变量引用一维数组元素
例如:int*p,a[5]; p=a;
说明:①p[0]表示p指针指向的内存单元,p[1]表示p指向的内存单元
②a[i]的等价引用方法:a[i]、p[i]、*(a+i)、*(p+i)
③a[i]的地址等价引用方法:&a[i]、&p[i]、a+i、p+i
例1.(09-03—29)若有以下定义
int x[10],*pt=x;
则对x数组元素的正确应用是 B
A)*&x[10] B)*(x+3) C)*(pt+10) D)pt+3
【解析】引用数组元素时,注意取地址运算符&与指针运算符t的作用;指针运算符+用来取得指针变量所指存储空间的内容,取地址运算符&用来取得变量的地址值;A选项数组下标越界;B选项中+(x+3)等价于元素X[3];C选项中。(pt+10)等价于x[10],数组下标越界;D选项pt+3是元素x[3]的地址,与&x[3]等价;故正确答案是B。正确答案:B
例1.(2011-09-27)有以下程序
#include <stdio.h>
void fun(int *p)
{printf(“%d\n”,p[5]);}
main()
{int a[10]={1,2,3,4,5,6,7,8,9,10};
fun(&a[3]);
}程序运行后的输出结果是 (D)
A.5 B.6 C.8 D.9
【解析】fun函数被调用时将&a[3]通过传递付给了形参指针变量p,此时可用*p或p[0]来表示a[3],因此p[5]亦可表示a[8],所以输出结果为9
7.知识点:指针与二维数组
l 任何一个二维数组均由若干个一维数组组成,a[0]、a[1]和a[2]是一维数组名,数组名代表数组的首地址,因此a[0]就代表数组元素a[0][0]的地址,也即&a[0][0]。
注意:a的值与a[0]相同,但它们的基类型不同,a可以等价于一个二维指针,而a[0]是一维指针。因此 int a[3][4],*p=a;错误
取数组元素a[i][j]的地址的几种方法:&a[i][j];a[i]+j; *(a+i)+j;
l 指针数组的定义方式:
*指针数组名[常量表达式];如:int *p[3];
l 行指针的一般定义形式如下:
类型名(*指针数组名)[常量表达式]; 如:int (*p)[2];
l 指针数组与行指针的区别
1、int *p[3];定义的是指针数组,表示一个数组,含有3个元素p[0]、p[1]、p[2],且这3个元素只能存放整型元素的地址
2、int (*p)[3];定义的是行指针,表示一个指针变量,它仅有一个存储空间,只能存放一个长度为2的一维数组指针。
例1.(06—09—33)若有定义语句:int k[2][3],*pk[3];则以下语句正确的是(B)
A)pk=k; B)pk[0]=&k[1][2]; C)pk=k[0]; D)pk[1]=k;
【解析】题目中定义了一个二维数组和一个指针数组,pk是指针数组名,不能被赋值,指针数组pk中的元素是指针,并且二维数组名是指向一维数组的指针常量,相当于行指针,二者不可转换,所以A、C、D选项均有误,本题答案选B。
例2.(09-09-27) 若有定义语句: int a[4][10],*p,*q[4];且0<=i<4,则错误的赋值是 (A)
A)p=a B)q[i]=a[i] C)p=a[i] D)p=&a[2][1]
【解析】p为基类型为int的指针,指向一个整形数据,也就可以指向一个数组元素,所以D正确。指针数组q的每个数组元素q[i]的基类型也为int,所以p、a[i]、a[i]的基类型一致,选项B、C也是正确的。
8.知识点:指针与函数
例1.(08—04—40)设有定义语句int(*f)(int);,则以下叙述正确的是__B_______。
A)f是基类型为int的指针变量
B)f是指向函数的指针变量,该函数具有一个int类型的形态
C)f是指向int类型一维数组的指针变量
D)f是函数名,该函数的返回值是其类型为int类型的地址
【解析】在c语言中,函数名代表此函数的入口地址,所因此,可以定义一种指向函数的指针来存放函数的入口地址,定义方式是:函数返回值类型(*函数指针名)(参数类型列表);本题答案选B。
9.知识点:指针与字符串
l 可以通过字符指针来处理字符串
例如:char *p="China"或者char *p;p="China";把字符串赋值给指针p,实质是把保存字符串"China"的那段内存的首地址赋值给指针p,使得指针p指向了字符串,这样就可以通过指针来操作字符串了。
【注意】char str[10] ; str="China";是错误的!数组名是地址常量,不能进行赋值操作!
l “三名主义”(考试的重点)
数组名:表示第一个元素的地址。数组名是地址常量名,不能进行赋值和自加(减)等运算。(考了很多次)
函数名:表示该函数的入口地址。
字符串常量名:表示第一个字符的地址。
例11.(2011-09-22)有以下程序(注:字符a的ASCII码值为97)
#include <stdio.h>
main()
{ char *s={“abc”};
do
{printf(“%d”,*s%10);++s;}
while(*s);
}程序运行后的输出结果是 (B)
A.abc B.789 C.7890 D.979898
例11.(2011-09-30)若有定义语句:char*s1=”OK”;*s2=”ok”;,以下选项中,能够输出“OK”的语句是 (D)
A.if(strcmp(s1,s2)==0) puts(s1);
B.if(strcmp(s1,s2)!=0) puts(s2);
C.if(strcmp(s1,s2)==1) puts(s1);
D.if(strcmp(s1,s2)!=0) puts(s1);
【解析】strcmp函数作为字符串比较函数,当s1等于s2所指字符串时结果为0,当s1大于s2所指字符串时结果为 >0,当s1小于s2所指字符串时结果为 <0
例11.(07—04—44)有下列程序:
main()
{char ch[]=“uvwxyz”,*pc;
pc=ch;printf(”%c\n”,*(pc+5));
} 程序运行后的输出结果是(A)
A)Z B)0 C)元素ch[5]的地址 D)字符Y的地址
【解析】指针P指向了字符数组ch,*(pc+5)-->ch[5]-->’z’,printf函数要求以%c形式输出,故输出字符Z。
例12.(09-09-37)设有定义:char *c;.以下选项中能够使字符型指针c正确指向一个字符串的是(A)
A)char str[]=”string”; c=str; B)scanf(“%s”,c);
C)c=getchar(); D)*c=”string”; 【解析】选项A为正确用法。先将字符串存于字符数组中,然后将数组名赋给字符指针。选项B为无语法错误,但运行时可能会出现问题。原因是字符指针没有被赋值,是一个不确定的值,指向一个不确定的内存区域,这个区域可能存放有用的指令或数据。在这个不确定的区域重新存放输入的字符串,可能会发生无法预知的错误。选项C错误。getchar()函数输入一个字符给字符型变量,而不应该是字符指针。选项D错误。*c=”string”应该改为c=”string”才是正确的。