1
第十章 指 针本章内容:
1.地址和指针的概念
2.变量的指针和指向变量的指针变量
3.数组的指针和指向数组的指针变量
4.字符串的指针和指向字符串的指针变量
5.函数的指针和指向函数的指针变量
6.指针数组和指向指针的指针
2
指针是C语言中广泛使用的一种数据类型 。
运用指针编程是C语言最主要的风格之一 。
利用指针变量可以表示各种数据结构;能很方便地使用数组和字符串;并能象汇编语言一样处理内存地址,从而编出精练而高效的程序 。
指针极大地丰富了C语言的功能 。 学习指针是学习C语言中最重要的一环,能否正确理解和使用指针是我们是否掌握C语言的一个标志 。
指针也是C语言中最为困难的一部分,在学习中除了要正确理解基本概念,还必须要多编程,上机调试 。
概 述:
3
计算机中所有的数据都是存放在存储器中的。 一般把存储器中的一个字节称为一个内存单元。
不同的数据类型所占用的内存单元数不等,如整型量占 2个单元,字符量占 1个单元。
为了正确地访问这些内存单元,必须为每个内存单元编上号。
根据一个内存单元的编号即可准确地找到该内存单元。
内存单元的编号也叫做地址。根据内存单元的编号 (地址 )就可以找到所需的内存单元,所以常把这个地址称为指针。
1、地址:内存单元的编号,通常一个字节( 1B)一个编号。
2、指针:内存单元的地址。
地址和指针的概念
4
3、内存单元的指针和内存单元的内容是两个不同的概念
4、内存数据的访问方式:
( 1)直接访问 —— 按变量名存取变量
( 2)间接访问 —— 将变量的地址存放在另一个变量(指针变量),通过指针变量来访问。
在C语言中允许用一个变量来存放指针,这种变量称为指针变量。
一个指针变量的值就是某个内存单元的地址或称为某内存单元的指针。
5
……..
30
50
60
变量 i
变量 j
变量 k
2000
2004
2002
2000 指针变量 ip
地址
6
严格地说,一个指针是一个地址,是一个常量 。
而一个指针变量却可以被赋予不同的指针值,是变量 。
但常把指针变量简称为指针 。
为了避免混淆,我们约定:,指针,是指地址,是常量,
,指针变量,是指取值为地址的变量 。
定义指针变量的目的是为了通过指针去访问内存单元 。
注意:
7
变量的指针和指向变量的指针变量变量的指针 —— 变量的地址例,&a 为变量 a在内存单的存放地址,也称变量 a的指针指针变量 —— 存放地址的变量指向变量的指针变量 —— 一个指针变量存放变量 a的地址,
则称该指针变量为指向变量 a的指针变量。
例,int a=10;
int *pa;
pa=&a; 10
a
&a
pa
8
指针变量的定义其一般形式为:
类型说明符 *变量名;
*表示这是一个指针变量,变量名即为定义的指针变量名;
类型说明符表示本指针变量所指向的变量的数据类型 (基类型 )。
例如,int *p1;
表示 p1是一个指针变量,它的值是某个整型变量的地址 。
或者说 p1指向一个整型变量 。
至于 p1究竟指向哪一个整型变量,应由向 p1赋予的地址来决定 。
指针变量的定义
9
static int *p2; /*p2是指向静态整型变量的指针变量 */
float *p3; /*p3是指向浮点变量的指针变量 */
char *p4; /*p4是指向字符变量的指针变量 */
再如:
注意:
一个指针变量只能指向同类型的变量,如 p3 只能指向浮点变量,不能时而指向一个浮点变量,时而又指向一个字符变量。
这是由于指针的移动和运算都与基类型有关。
10
指针变量同普通变量一样,使用之前不仅要定义说明,而且必须赋予具体的值 。 未经赋值的指针变量不能使用,否则将造成系统混乱,甚至死机 。
指针变量的赋值只能赋予地址,不能赋予任何其它数据,
否则将引起错误 。
C语言中提供了地址运算符 &来表示变量的地址 。
其一般形式为,& 变量名;
例如,&a变示变量 a的地址,&b表示变量 b的地址 。
变量 a本身必须预先说明 。
指针变量的赋值
11
(1)指针变量初始化的方法:
int a;
int *p=&a;
设有指向整型变量的指针变量 p,如要把整型变量 a 的地址赋予 p可以有以下两种方式:
(2)赋值语句的方法:
int a,*p;
p=&a;
12
指针变量可以进行某些运算,但其运算的种类是有限的,
只能进行赋值运算和部分算术运算及关系运算 。
1,指针运算符
(1)取地址运算符 &
单目运算符,其结合性为自右至左,其功能是取变量的地址 。 例,&a 即取变量 a的地址
(2)取内容运算符 *
单目运算符,其结合性为自右至左,其功能是用来表示指针变量所指的变量 。
在 *运算符之后跟的变量必须是指针变量 。
指针变量的运算(引用)
13
例,void main()
{ int a=5,*p=&a;
printf ("%d",*p);
}
注意:
指针运算符 *和指针变量说明中的指针说明符 * 意义是不相同。
在指针变量说明中,*是类型说明符,表示其后的变量是指针类型。
表达式中出现的 *则是一个运算符用以表示指针变量所指的变量。
两个 *的意义相同么?
14
若有定义,int a,*p=&a
( 1) &*p 表示,p (a的地址 )
( 2) *&a 表示,a
( 3) (*p)++ 相当于 a++
( 4) *p++ 相当于 *( p++),即先取 p所指向变量的值,
然后,让 p指向下一个存储单元 。
( 5) *++p 相当于 *( ++p) 即先让 p指向下一个存储单元,然后再取 p所指向变量的值 。
说明:
15
指针变量的赋值运算有以下几种形式:
① 指针变量初始化赋值,前面已作介绍 。
② 把一个变量的地址赋予指向相同数据类型的指针变量 。
例如,int a,*pa;
pa=&a; /*把整型变量 a的地址赋予整型指针变量 pa*/
③ 把一个指针变量的值赋予指向相同数据类型变量的另一个指针变量 。
2,赋值运算
16
void main()
{ int a=1,b=2,*pa=&a,*pb=&b;
pa=pb;
printf("a=%d\nb=%d\n*pa=%d\n*pb=%d\n",a,b,*pa,*pb);
}
例如:
1
a
&b
&b
pb
2
b
pa
执行 pa=pb;
1
a
&a
&b
pb
2
b
pa
初始化若改为 *pa=*pb;
有何不同?
17
void main()
{ int a=1,b=2,*pa=&a,*pb=&b;
*pa=*pb;
printf("a=%d\nb=%d\n*pa=%d\n*pb=%d\n",a,b,*pa,*pb);
}
例如:
1
a
&a
&b
pb
2
b
pa
初始化
2
a
&a
&b
pb
2
b
pa
执行 *pa=*pb;
18
④ 把数组的首地址赋予指向数组的指针变量 。
例如,int a[5],*pa;
pa=a; (C语言规定数组名表示数组的首地址 )
也可写为,pa=&a[0];
数组第一个元素的地址也是整个数组的首地址,可赋予 pa
指针变量 。
当然也可采取初始化赋值的方法:
int a[5],*pa=a;
指针变量的赋值运算
19
⑤ 把字符串的首地址赋予指向字符类型的指针变量。
例如,char *pc;
pc="C language";
或用初始化赋值的方法写为:
char *pc="C Language";
这里应说明的是并不是把整个字符串装入指针变量,而是把存放该字符串的字符数组的首地址装入指针变量。
输出字符串是可以使用% s格式字符串,即语句
printf(“%s”,pc);
指针变量的赋值运算
20
指针变量可以赋予 0值,p=0表明 p是空指针,它不指向任何变量在头文件 "stdio.h"中有,#define NULL 0
因此在 C程序中常使用 int *p=NULL; 让指针变量 p为空指针 。
对指针变量赋 0值和不赋值是不同的 。 指针变量未赋值时,
可以是任意值,是不能使用的 。 否则将造成意外错误 。
而指针变量赋 0值后,则可以使用,只是它不指向具体的变量而已 。
说 明:
21
void main()
{ int a,b,*p,*p1=&a,*p2=&b;
scanf("%d%d",p1,p2);
if(a<b) { p=p1; p1=p2; p2=p;}
printf("\na=%d,b=%d\n\n",a,b);
printf("max=%d,min=%d\n",*p1,*p2); }
例如:输入 a,b两个整数,按先大后小的顺序输出
5
a
&a
p1
&b
p2
9
b
5
a
&b
p1
&a
p2
9
b
p
22
指针变量作为函数参数在第八章已学过:函数的参数可以是:整型、实型、字符型等数据。
C语言中函数的参数还可以是指针类型,其作用是用来传递一个变量的地址。
23
void sawp(int *x,int *y)
{ int temp;
temp=*x; *x=*y; *y=temp;
}
void main( )
{ int a,b,*p,*p1=&a,*p2=&b;
scanf("%d%d",p1,p2);
printf("\na=%d,b=%d\n\n",a,b);
sawp(p1,p2);
printf("\na=%d,b=%d\n\n",a,b);
}
例,写一交换两个变量值的函数 sawp()
24
程序的执行过程
5
a
&a
p1
p2
9
b
&b
调用前
5
a
&a
p1
x
&a
9
b
&b
p2
y
&b
调用时
9
a
&a
p1
x
&a
5
b
&b
p2
y
&b
函数 sawp()执行中
9
a
&a
p1
p2
5
b
&b
调用结束后
sawp(int *x,int *y)
{
int temp;
temp=*x;
*x=*y;
*y=temp;
}
25
void sawp(int *x,int *y)
{
int *temp;
*temp=*x;
*x=*y;
*y=*temp;
}
此语句不对,因指针 temp
变量没有明确指向若将 sawp函数写为如下:
26
若将 sawp函数写为如下:
void sawp(int x,int y)
{ int temp;
temp=x;
x=y;
y=temp;
}
void main()
{ int a,b;
scanf("%d%d",&a,&b);
printf("\na=%d,b=%d\n\n",a,b);
sawp(a,b);
printf("\na=%d,b=%d\n\n",a,b);
}
27
程序的执行过程
5
a
9
b
调用前
x 5
调用时
5a
y 9
9b 调用结束后
5
a
9
b
函数 sawp()执行中
x
5a
y
9b
5
9
sawp(int x,int y)
{ int temp;
temp=x;
x=y;
y=temp;
}
28
void sawp(int *x,int *y)
{ int *temp;
temp=x; x=y; y=temp;
}
void main( )
{ int a,b,*p,*p1=&a,*p2=&b;
scanf("%d%d",p1,p2);
printf("\na=%d,b=%d\n\n",a,b);
sawp(p1,p2);
printf("\na=%d,b=%d\n\n",a,b);
}
再如:
29
程序的执行过程
5
a
&a
p1
p2
9
b
&b
调用前
5
a
&a
p1
x
&a
9
b
&b
p2
y
&b
调用时
5
a
&a
p1
x
&b
9
b
&b
p2
y
&a
函数 sawp()执行中
5
a
&a
p1
p2
9
b
&b
调用结束后
sawp(int *x,int *y)
{ int *temp;
temp=x;
x=y;
y=temp;
}
30
说明:
形参和实参之间的数据传递是单向的,值传递,,指针变量做函数参数也要遵循这一原则。
调用函数不可能改变实参指针变量的值,但可以改变实参指针变量所指向的变量的值。
31
一个数组是由连续的一块内存单元组成的 。 C语言规定:
数组名就是这块连续内存单元的首地址 ( 数组的指针 ) 。
一个数组也是由各个数组元素 (下标变量 ) 组成的 。 每个数组元素按其类型不同占有几个连续的内存单元 。
一个指针变量既可以指向一个数组,也可以指向一个数组元素,可把数组名或第一个元素的地址赋予它 。
要使指针变量指向第 i号元素可以把第 i号元素的首地址赋予它或把数组名加 i赋予它 。
数组的指针和指向数组的指针变量
32
例,int a[10],*p=a;
p,a,&a[0]均指向同一单元,
它们是数组 a的首地址,也是第 0号元素 a[0]的首地址 。
p+1,a+1,&a[1]均指向第 1号元素 a[1]。
以此类推可知 p+i,a+i,&a[i]
指向第 i号元素 a[i]。
1 a[0]
a[1]
a[2]
a[3]
a[9]
5
3
7
9
19
p,a,&a[0]
a[i]
p+i,a+i,&a[i]
p+1,a+1,&a[1]
注意,p是指针变量,而 a,&a[i]都是常量 。
指向数组元素的指针
33
引入指针变量后,就可以用两种方法来访问数组元素了 。
第一种方法为下标法,即用 a[i]形式访问数组元素 。
通过指针引用数组元素
void main()
{ int a[5],i;
for(i=0;i<5;i++)
scanf("%d",&a[i]);
for(i=0;i<5;i++)
printf("a[%d]=%d\n",i,a[i]);
}
34
void main()
{ int a[5],i,*pa=a;
for(i=0;i<5;i++)
{ scanf("%d",pa);
pa++;
}
for(i=0;i<5;i++)
{ printf("a[%d]=%d\n",i,*pa);
pa++;
}
}
第二种方法为指针法,即采用 *(a+i)或 *(p+i)形式,用间接访问的方法来访问数组元素。
分析程序中存在的问题
pa=a;
a[2]
1 a[0]a[1]
a[3]5
3
7
9 a[4]

pa
pa a[5]
35
void main()
{ int a[5],i;
for(i=0;i<5;i++) scanf("%d",a+i);
for(i=0;i<5;i++) printf("a[%d]=%d\n",i,*(a+i));
}
三种方式的比较:
( 1)下标法比较直观,但执行效率低。
( 2) a[i] 编译时转换为 *(a+i),计算出元素的地址。
( 3)使用指针变量引用元素,用 p++指向下一个元素,因执行效率较高。
第二种方法为指针法,即采用 *(a+i)或 *(p+i)形式,用间接访问的方法来访问数组元素。
36
1、指针变量可实现本身值的改变例如,int a[10],*p=a
p++ 指向 a[1]元素可以使用下面语句来输入数组元素。
for(p=a;p<a+10;p++) scanf("%d",p);
使用指针变量引用数组元素时要注意指针变量当前的指向,
否则将出错。
a++; a=&a[i]; a=p;×× ×
使用指针变量要注意的几个问题:
2、数组名表示数组首地址,是地址常量,不能出现如下运算:
37
设有定义,int a[10],*p=a;
(1) p++ 指向下一个元素,p的实际值加 sizeof(int) 2,
(2) *p++ 等价于 *(p++)
(3) *(p++) 与 *(++p)不同
(4) (*p)++ 表示 p当前所指向元素的值加 1
(5) 若 p=&a[i] 则,
*(p - -) 相当于 a[i - -] *(- -p) 相当于 a[ - -i]
*(p++) 相当于 a[i++] *(++p) 相当于 a[++i]
p - a 的值为 i
注意指针变量的运算:
38
在第八章 ( 函数 ) 中曾经介绍过用数组名作函数的实参和形参的问题,在学习指针变量之后就更容易理解这个问题了 。
数组名就是数组的首地址,实参向形参传送数组名实际上就是传送数组的地址,形参得到该地址后也指向同一数组 。
同样,指针变量的值也是地址,数组的指针即为数组的首地址,当然也可作为函数的参数使用 。
数组名和数组指针变量作函数参数
39
float aver(float *pa)
{ int i; float av,s=0;
for(i=0;i<5;i++) s=s+*pa++;
av=s/5;
return av;
}
void main()
{ float sco[5],av,*sp; int i;
sp=sco;
printf("\ninput 5 scores:\n");
for(i=0;i<5;i++) scanf("%f",&sco[i]);
av=aver(sp);
printf("average score is %5.2f",av);
}
例如:
40
在 C语言中形参和实参都可使用指针变量和数组名:
1、当形参是数组时,要求对应的实参为:
同类型的数组名或指针变量
2、当形参是指针变量,要求对应的实参为:
同类型的指针变量或数组名形式参数指针变量数组名实际参数指针变量数组名


③ ④
说明:
41
void inv(int x[ ],int n)
{ int t,i,j,m=(n-1)/2;
for(i=0;i<=m;i++)
{ j=n-i-1;
t=x[i];x[i]=x[j];x[j]=t;}
}
void main()
{ int i,a[10];
for (i=0;i<10;i++) scanf("%d",&a[i]);
inv(a,10);
for (i=0;i<10;i++) printf("%d ",a[i]);
}
例,将数组 a中的元素按相反顺序存放。
方法一:
数组名做形参数组名做实参
42
void inv(int x[ ],int n)
{ int t,i,j,m=(n-1)/2;
for(i=0;i<=m;i++)
{ j=n-i-1;
t=x[i];x[i]=x[j];x[j]=t;}
}
void main()
{ int i,a[10],*p=a;
for (i=0;i<10;i++) scanf("%d",p++);
p=a;
inv(p,10);
for (i=0;i<10;i++) printf("%d ",*p++);
}
方法二:
数组名做形参指针变量做实参指针变量做实参,
一定要先使其有确定值
43
void inv(int *x,int n)
{ int t,i,j,m=(n-1)/2;
for(i=0;i<=m;i++)
{ j=n-i-1;
t=*(x+i);
*(x+i)=*(x+j);
*(x+j)=t; }
}
void main()
{ int i,a[10];
for (i=0;i<10;i++) scanf("%d",&a[i]);
inv(a,10);
for (i=0;i<10;i++) printf("%d ",a[i]);
}
方法三:
指针变量做形参数组名做实参
44
void inv(int *x,int n)
{ int t,i,j,m=(n-1)/2;
for(i=0;i<=m;i++)
{ j=n-i-1;
t=*(x+i);
*(x+i)=*(x+j);
*(x+j)=t; }
}
void main()
{ int i,a[10],*p=a;
for (i=0;i<10;i++) scanf("%d",p++);
p=a;
inv(p,10);
for (i=0;i<10;i++) printf("%d ",*p++);
}
方法四:
指针变量做形参指针变量做实参
45
多维数组地址的表示方法若有定义,int a[3][4]={{0,1,2,3},{4,5,6,7},{8,9,10,11}};
0 1 2 3
4 5 6 7
8 9 10 11
a[0]
a[1]
a[2]
a
a+2
a+1
a[0] a[0]+1a[0]+2a[0]+3
2000
2016
2008
2000数组名 a为数组首地址,2000
指向多维数组的指针变量
46
a 二维数组首地址,第 0行首地址
a[0],*(a+0),*a,&a[0][0] 第 0行第 0列元素地址
a+1,&a[1] 第 1行首地址
a[1],*(a+1),&a[1][0] 第 1行第 0列元素地址
a[1]+2,*(a+1)+2,&a[1][2] 第 1行第 2列元素地址
a+i,&a[i] 第 i行首地址
a[i],*(a+i),&a[i][0] 第 i行第 0列元素地址
a[i]+j,*(a+i)+j,&a[i][j] 第 i行第 j列元素地址二维数组的地址表示:
符号 &将列指针变为行指针,而 *则将行指针变为列指针
47
设有定义,int a[3][4],i,j;
( 1)下标法,a[i][j]
( 2)指针法,*(*(a+i)+j) 或者 *(a[i]+j)
引用二维数组元素的方法:
48
多维数组的指针变量
1,指向数组元素的指针变量设有定义,int a[3][4],i,j; int *p=&a[0][0];
p为指向数组元素的指针变量,引用二维数组元素的方法:
p+1则表示指向当前元素的下一个元素元素 a[i][j]的地址表示为,p+4*i+j
元素 a[i][j]的值表示为,*(p+4*i+j)
如果一个 n*m的二维数组的第 i 行第 j 列元素相对于首地址的增量为,i*m+j
以上可以体现出数组下标从 0开始的优点。
49
void main()
{ int a[3][4]={0,1,2,3,4,5,6,7,8,9,10,11};
int i,*p;
p=&a[0][0];
for(i=0;i<12;i++)
{ if (i%4==0) printf("\n");
printf("adr=%o,value=%2d\n",p,*p++);
}
}
例,用指针变量输出数组元素的值改成 p=&a[0];或者
p=a;可否?
为什么?
50
因为 n× m的二维数组 a可分解为一维数组 a[0],a[1],..,a[m-1],
而它们分别又是具有 m个元素的一维数组 。
因此可定义一个指向由 m个元素组成的一维数组的指针变量来处理二维数组 。
这个指针变量称为:行指针变量行指针变量说明的一般形式为:
类型说明符 (*指针变量名 )[长度 ]
例,int (*p)[4] 它表示 p是一个指针变量,它指向由 4个元素组成的一维数组,或说 p可指向一个有 4列的二维数组 。
2、指向由 m个元素组成的一维数组的指针变量
51
0 1 2 3
4 5 6 7
8 9 10 11
a[0]
a[1]
a[2]
a,p
a+2,p+2
a+1,p+1
*p+0 *p+1*p+2 *p+3
若 int a[3][4]= {0,1,2,3,4,5,6,7,8,9,10,11};
int i,j,(*p)[4]; p=a;
数组 a的元素 a[i][j]的表示,*(*(a+i)+j),*(*(p+i)+j)
*(a[i]+j),*(p[i]+j)
52
void average(float *p,int n)
{float sum=0,aver; float *p_end=p+n-1;
for(;p<=p_end;p++) sum =sum+(*p);
aver=sum/n;
printf("average=%5.2f\n",aver);
}
void search(float (*p)[4],int n)
{int i; printf("the score of No.%d are:\n",n);
for(i=0;i<4;i++) printf("%5.2f ",*(*(p+n)+i));
}
void main()
{ float score[3][4]={{65,77,65,85},{80,87,90,81},{99,100,78,85}};
average(*score,12);
search(score,2);
}
列指针行指针
53
指向字符串的指针变量的定义与指向字符变量的指针变量说明是相同的。只能按对指针变量的赋值不同来区别。
对指向字符变量的指针变量应赋予该字符变量的地址。
char c,*p=&c; 表示 p是一个指向字符变量 c的指针变量 。
而 char *s=“C Language”; 则表示 s是一个指向字符串的指针变量,把字符串的首地址赋予 s。
例,main()
{char *ps;
ps="C Language";
printf("%s",ps);
}
char *ps="C Language";
字符串的指针和指向字符串的指针变量
54
void main()
{ char *ps="this is a book";
int n=10;
ps=ps+n;
printf("%s\n",ps);
}
程序运行的结果为?
例:输出字符串中 n个字符后的所有字符。
book
55
void main()
{ char a[]="this is a book",b[16];
int n;
for(n=0;a[n]!='\0';n++)
*(b+n)=*(a+n);
*(b+n)='\0';
printf("%s\n",b);
}
例:字符串的复制
56
T
i
h
s
p1
p1
i
s
a
b
o
k
\0
o
a
T
i
h
s
p2
p2
i
s
a
b
o
k
\0
o
b
void main()
{ char a[]="this is a book";
char b[16],*p1,*p2;
p1=a;p2=b;
while(*p1!='\0')
*p2++=*p1++;
*p2='\0';
p2=b;
printf("%s\n",p2);
}
例:字符串的复制
57
void main()
{ char *p1="this is a book ";
char *p2;
while (*p1!='\0')
.,,/* 以下与上面相同 */
}
问能否将程序写成如下形式?
危险的使用
58
字符串指针作为函数的参数把字符串指针作为函数参数使用,传递字符串或字符数组的首地址 。
1、当形参是字符数组时,要求对应的实参为:
字符数组名或字符指针变量
2、当形参是字符指针变量,要求对应的实参为:
字符数组名或字符指针变量形式参数字符指针变量字符数组名实际参数字符指针变量字符数组名


③ ④
59
void cpystr(char *pss,char *pds)
{ while((*pds=*pss)!='\0')
{ pds++;
pss++; }
}
void main()
{ char *pa="CHINA",b[10],*pb;
pb=b;
cpystr(pa,pb);
printf("string a=%s\nstring b=%s\n",pa,pb);
}
while((*pds++=*pss++)!='\0');
例:要求把一个字符串的内容复制到另一个字符串中,并且不能使用 strcpy函数。
60
1,字符串指针变量本身是一个变量,用于存放字符串的首地址 。 而字符串本身是存放在以该首地址为首的一块连续的内存空间中并以 ‘ \0’作为串的结束 。 字符数组是由于若干个数组元素组成的,它可用来存放整个字符串 。
2,对字符数组作初始化赋值,必须采用外部类型或静态类型,如,static char st[]={“C Language”};
而对字符串指针变量则无此限制,如,char *ps="C
Language";
使用字符数组和字符指针变量处理字符串时应注意的问题:
61
3,对字符串指针方式 char *ps="C Language";
可以写为,char *ps; ps="C Language";
而对数组方式,static char st[]={"C Language"};
不能写为,char st[20]; st={"C Language"};
而只能对字符数组的各元素逐个赋值。
使用字符数组和字符指针变量处理字符串时应注意的问题:
62
C语言中规定,一个函数总是占用一段连续的内存区,
而函数名就是该函数所占内存区的首地址。
函数的首地址 (或称入口地址 )称为函数的指针。
把函数的指针赋予一个指针变量,使该指针变量指向该函数,然后通过指针变量就可以找到并调用这个函数。
指向函数的指针变量称为指向函数的指针变量,也称函数指针变量。
函数的指针和指向函数的指针变量
63
类型说明符 (*指针变量名 )( );
类型说明符表示被指函数的返回值的类型。
(* 指针变量名 )表示 *后面的变量是定义的指针变量。
空括号表示指针变量所指的是一个函数。
一、函数指针变量定义的一般形式为:
例如,int (*pf)( );
表示 pf是一个指向函数入口的指针变量,该函数的返回值 (函数值 )是整型。
64
int max(int a,int b)
{ if(a>b)return a;
else return b;
}
void main()
{ int (*pmax)(int,int);
int x,y,z;
pmax=max;
printf("input two numbers:\n");
scanf("%d%d",&x,&y);
z=(*pmax)(x,y);
printf("maxmum=%d\n",z);
}
命令 1
pmax
命令 2
.,,
下面通过例子来说明用指针形式实现对函数调用的方法。
65
1,先定义函数指针变量,如,int (*pmax)();
定义 pmax为函数指针变量 。
2,把被调函数的入口地址 (函数名 )赋予该函数指针变量,如程序中的 pmax=max;
3,用函数指针变量形式调用函数,如程序中 z=(*pmax)(x,y);
用函数指针变量形式调用函数的步骤如下:
66
(*指针变量名 ) (实参表 )
使用函数指针变量还应注意以下两点:
a,函数指针变量不能进行算术运算,这是与数组指针变量不同的 。 数组指针变量加减一个整数可使指针移动指向后面或前面的数组元素,而函数指针的移动是毫无意义的 。
b,函数调用中 (*指针变量名 )的两边的括号不可少,其中的 *
不应该理解为求值运算,在此处它只是一种表示符号,代替函数名 。
指针形式调用函数的一般形式
67
用指向函数的指针作函数参数函数指针变量的用途就是把指针作为参数传递到其他函数。
例如:
Sub(int (*x1)(int),int (*x2)(int))
{int a,b,i,j;
a=(*x1)(i);
b=(*x2)(j);
……
}
其中 x1指向函数 f1,x2指向函数 f2
68
为什么不直接调用 f1和 f2呢?
如果每次要调用的函数不同,则不需要修改 sub函数,
只需在调用时给不同的函数入口地址即可实现。
这样做可以增加程序的灵活性,可以通过编制一个通用的程序来实现各种专用的功能。
注意:
69
返回指针值的函数所谓函数类型是指函数返回值的类型 。
在C语言中允许一个函数的返回值是一个指针 (即地址 ),这种返回指针值的函数称为指针型函数 。
定义指针型函数的一般形式为:
类型说明符 *函数名 (形参表 )
{ …… /*函数体 */
}
函数名之前加了 *号表明这是指针型函数,即返回值是指针 。
类型说明符表示了返回的指针值所指向的数据类型 。
70
int *ap(int x,int y)
{
.....,/*函数体 */
}
ap是一个返回指针值的指针型函数 。
返回的指针指向一个整型变量 。
例如:
71
int (*p)()和 int *p()是两个完全不同的量。
int (*p)()是一个变量说明,说明 p 是一个指向函数入口的指针变量,该函数的返回值是整型量。
int *p() 则不是变量说明而是函数说明,说明 p是一个指针型函数,其返回值是一个指向整型量的指针,*p两边没有括号。
注意函数指针变量和指针型函数在写法和意义上的区别。
72
char *day_name(int n)
{ char*name[]={" Error","Monday","Tuesday",
"Wednesday","Thursday","Friday",
"Saturday","Sunday"};
return((n<1||n>7)? name[0],name[n]);
}
void main()
{ int i;
printf("input Day No:\n");
scanf("%d",&i);
printf("Day No:%2d-->%s\n",i,day_name(i));
}
例如:
73
指针数组的概念一个数组的元素值为指针则这个数组称为是指针数组 。
指针数组是一组有序的指针的集合 。
指针数组的所有元素都必须是具有相同存储类型和指向相同数据类型的指针变量 。
指针数组说明的一般形式为:
类型说明符 *数组名 [数组长度 ]
其中类型说明符为指针值所指向的变量的类型 。
例如,int *pa[3]; 表示 pa是一个指针数组,它有三个数组元素,每个元素值都是一个指针,指向整型变量 。
指针数组和指向指针的指针
74
#include "string.h"
void main()
{ void sort(char *name[],int n);
void print(char *name[],int n);
char *name[]={ "CHINA","AMERICA",
"AUSTRALIA","FRANCE","GERMAN"};
int n=5;
sort(name,n);
print(name,n);
}
例:由字符指针数组处理多个字符串的问题
GERMAN
FRANCE
AUSTRALIA
AMERICA
CHINA
name[4]
name[3]
name[2]
name[1]
name[0]
75
void sort(char *name[],int n)
{ char *pt; int i,j,k;
for(i=0;i<n-1;i++)
{ k=i;
for(j=i+1;j<n;j++)
if(strcmp(name[k],name[j])>0) k=j;
if(k!=i)
{ pt=name[i]; name[i]=name[k]; name[k]=pt;}
}
}
void print(char *name[],int n)
{ int i;
for (i=0;i<n;i++) printf("%s\n",name[i]);
}
GERMAN
FRANCE
AUSTRALIA
AMERICA
CHINA
name[4]
name[3]
name[2]
name[1]
name[0]
76
指针数组名同样表示指针数组的首地址,是一个指针变量的地址,即是指针的指针。
指针变量在内存中仍有一个存储单元,该单元的地址,即指针的指针。如下图:
a
10
p
&a
pp
&p
指向指针的指针变量的定义:
int a,*p=a;**pp=&p;
类型说明符 **指针名指向指针的指针
77
前面介绍的 main函数都是不带参数的 。 因此 main 后的括号都是空括号 。 实际上,main函数可以带参数,这个参数可以认为是 main函数的形式参数 。
C语言规定 main函数的参数只能有两个,习惯上这两个参数写为 argc和 argv。
因此,main函数的函数头可写为:
main (argc,argv)
规定 argc(第一个形参 )必须是整型变量。
argv( 第二个形参 )必须是指向字符串的指针数组。
main()函数的参数
78
加上形参说明后,main函数的函数头应写为:
main (argc,argv)
int argv; char *argv[];
或写成,main (int argc,char *argv[])
如命令行,rename a.cpp b.cpp”
b.cpp
a.cpp
rename
argv[2]
argv[1]
argv[0]argv
argc=3
main()函数的参数
79
实验九实验指导书实验八第 1题源文件命名为 sy9-1.cpp上传;
教材 Pg.258 10.8源文件命名为 sy9-2.cpp上传。
作业教材 Pg.258 10.3 10.6 10.15