首页 上页 下页 节 末页 结束
,C语言程序设计,
课程讲义
第九章 指针
2006年 4月
首页 上页 下页 节 末页 结束
1、掌握 C语言的函数的概念、定义和调用
2、掌握函数的嵌套调用和递归调用
3、掌握全局变量和局部变量以及自动变量、
静态变量和寄存器变量的特点和关系
上次课程的回顾
首页 上页 下页 节 末页 结束
9.1 地址和指针的概念
9.2 变量的指针和指向变量的指针变量
9.3 数组与指针
9.4 字符串与指针
9.5 指向函数的指针
9.6 返回指针值的函数
9.7 指针数组和指向指针的指针
第 9章 指针
首页 上页 下页 节 末页 结束
本章
重点
本节
难点
1、掌握指针变量的概念、定义和引用
2、掌握指向一维数组的指针变量和指向字符串的指针变量的
定义及使用
3、掌握数组元素和字符串中字符的指针变量的使用
4、掌握变量作函数参数与数组名或字符串作函数参数的关系
5、掌握指针数组处理若干字符串的方法
1、指针与数组的关系
2、指针变量作函数参数
3、指针数组的使用
4、指向指针的指针的使用
首页 上页 下页 节 末页 结束
一,指针概述,
1,地址的概念与取地址运算,
内存以字节编码,每个编码都是一个地址。我们原先学过
的变量、数组、函数等都放在内存中,至于它们放在内存的什么
地方,这都是机器的事,我们只要知道它们是以怎样的顺序放在
内存中的,以便一一按顺序引用。我们怎样知道机器将某种数据
放在内存的什么地方呢?可用求地址运算符 &
如,int a = 3 ;
&a 就是变量在内存中的地址。
可以用 printf(“%x \n”,&a); 看出其地址。 注意,这
个地址并不是始终不变的,这是由机器和操作系统来安排的,我
们无法预先知道。
9.1地址和指针的概念
首页 上页 下页 节 末页 结束
则 a表示的地址和 &a [0]的地址相同。
但 &不能施加在常数、常量或表达式上,也不能施加在寄存器
变量上(因为寄存器变量在 cpu中,不在内存中)。
二、指针变量,
既然存储在内存中的各种变量都有一个地址,我们能否这样设
想:定义某种变量,让这个变量的值始终等于某个变量的地址,
如同某个房间号、门牌号一样?回答是肯定的。我们把 这种存放
某种变量地址的变量称为指针变量。
10
35
…,
a
b
2010
2012
p 2010
q 2012
因此,在 C语言中,将地
址形象化地称为 指针
9.1地址和指针的概念
首页 上页 下页 节 末页 结束
说明 (系统对变量的访问形式分为两种 )
一个变量的访问(访问是指取出其值或向它赋值)方式有两
种,
( 1)直接访问,通过变量名访问,如通过变量名 i直接访问。
( 2)间接访问,通过该变量的指针来访问,如通过 指针 p访
问变量 i。
9.2 变量的指针和指向变量的指针变量
首页 上页 下页 节 末页 结束
基类型,即该指针变
量所指向的变量的类

三、定义指针变量,
[存储类型 ] 数据类型 * 指针变量名 1,* 指针变量
名 2,… ;
如,int *p,a = 3;
int *q,b=5;
如何使一个指针变量指向另
外一个变量呢?
P=&a;
q=&b;
10
35
…,
a
b
2010
2012
p 2010
q 2012
9.2 变量的指针和指向变量的指针变量
首页 上页 下页 节 末页 结束
为什么要为指针变量定义类型,
由于不同的数据有不同的数据类型,如 char仅占一
个字节,int 占两个字节,float型占三个字节,而内存
又是以字节为单位进行地址编号,因而对 char型,只
要地址数加减 1,取出里面的数据就是 char型的完整
数据。对 int型就不同了,要取出其中的数据需 2字节,
就不能对地址进行简单的加减 1了。怎样取出一个完整
的 int型数据或 float型数据呢?如果能够将指针变量也
定义为具有同样的数据类型,那么对指针进行加 1或减
1运算,就是让指针移动相应基类型对应的字节数。
9.2 变量的指针和指向变量的指针变量
首页 上页 下页 节 末页 结束
二、指针变量赋值(这里要用到取地址运算符,&”)
指针变量的赋值:例,
pointer_1 = &i;
pointer_2 = &j;
注意,指针变量中只能存放地址,不能将一个非地址类型的数据(如
常数等)赋给一个指针变量,如,pointer_1 = 100;
3.14
2.12
p
P+1
4byte
p
P+1
P+2
P+3
1
2
3
4
2byte
9.2 变量的指针和指向变量的指针变量
首页 上页 下页 节 末页 结束
也可以在定义指针变量的同时指定其初值,如,
int a;
int *p = &a;
三、指针变量的引用
1、取地址运算:如,int *p,i=3,j=5;
p=&i; /*让 p其指向 变量 i */
2、对指针变量施加 *运算,则代表取指针所指向的单元的内容。
这里,*p与变量 i是等价的。
区分,*运算符在不同场合的作用,编译器能够根据上下文环境判别 *的作用。
int a,b,c;
int * p; ( *表示定义指针)
p = &a;
*p = 100; ( *表示指针运算符)
c = a * b; ( *表示乘法运算符)
9.2 变量的指针和指向变量的指针变量
首页 上页 下页 节 末页 结束
printf(“%d \n”,*p) ;
p=&j;
printf(“%d \n”,*p) ;
但不能 int *p;
char c=?A? ;
p=&c ; (类型不匹配)
区分 *运算符的以下用法,
int a ;
int *p = &a; /* 定义指针变量时指定初值,是为 p指定初值 */
*p = 100; /* 给指针 p所指向的变量赋值,这里是给变量 a赋值 */
9.2 变量的指针和指向变量的指针变量
首页 上页 下页 节 末页 结束
例 9_1.c
main( )
{ int *p1,*p2,a1,a2;
scanf(“%d %d”,&a1,&a2 );
p1 =&a1; p2=&a2;
printf(“%d,%d \n”,*p1,*p2);
p2 = p1;
printf(“%d,%d \n”,*p1,*p2);
}
这里有一个运算符 *,其作用是:加在指针变量的前面,
取出该指针变量所指对象的内容。
9.2 变量的指针和指向变量的指针变量
首页 上页 下页 节 末页 结束
例 9_2.c 使两个指针变量交换指向。
main( )
{ int *p1,*p2,*p,a1=10,a2=20;
p1=&a1;
p2=&a2;
printf(“%d,%d \n,,*p1,*p2);
p=p1; p1=p2; p2=p;
printf(“%d,%d \n,,*p1,*p2);
}
9.2 变量的指针和指向变量的指针变量
首页 上页 下页 节 末页 结束
交换前
下面表示 p1和 p1交换所指内容
3
5
a1
a2
P1 &a1
P2 &a2
3
5
a1
*p2
a2
*p1
交换后
P1 &a2
P2 &a1
5
3
a1
*p1
a2
*p2
交换后
P1 &a1
P2 &a2
3
5
a1
*p1
a2
*p2
交换前
P1 &a1
P2 &a2
a=*p1;*p1=*p2;*p2=p
P=p1;p1=p2;p2=p
首页 上页 下页 节 末页 结束
例 9_3.c 交换两个指针变量所指向的变量的值。
main( )
{ int *p1,*p2,a1,a2,a;
a1=10;
a2=20;
p1=&a1;
p2=&a2;
a=*p1; *p1=*p2; *p2=a;
printf(“a1= %d,a2= %d \n,,a1,a2);
}
9.2 变量的指针和指向变量的指针变量
首页 上页 下页 节 末页 结束
9.3.1 指向数组元素的指针
一个数组是由连续的一块内存单元组成的。数组名就
是这块连续内存单元的首地址。一个数组也是由各个
数组元素 (下标变量 )组成的。每个数组元素按其类型
不同占有几个连续的内存单元。一个数组元素的首地
址也是指它所占有的几个内存单元的首地址。
定义一个指向数组元素的指针变量的方法,与以前
介绍的指针变量相同。
首页 上页 下页 节 末页 结束
1,元素赋值、取值
如,int a[10],*p; p=a;
*p=1; 相当于 a[0]=1;
1、各元素地址 (用上定义) 指针
p 增一便指向下一单元 。 所以
p 指向 a[0]
p+1 指向 a[1]
p+2 指向 a[2]
,
p+i 指向 a[i]
,
p+9 指向 a[9]
a[0]
a[1]
a[2]
a[3]
a[ i]
a[7]
a[8]
a[9]
p
p,( 2000)
p+1,a+1
p+2,a+2
p+3,a+3
p+i,a+i
p+7,a+7
p+8,a+8
p+9,a+9
1
2
3
4
8
9
0
2000
A数组 9.3.1 指向数组元素的指针
首页 上页 下页 节 末页 结束
9.3.1 指向数组元素的指针
2、引用元素
直接引用 a[0]=1; a[i ]=6; 通过地址应用
如,*p=1; ….,*(p+i)=6;
或者 *a=1; ….,*(a+i)=6;
, [ ]” 实际是 变址 运算符。 如 a[i ]在这里变址的值为
i*2,,2”
是整型数据占两个字节。
由于有,p=a,因此 p==a,p+i 与 a+i 等价
所以有,
a 指向 a[0]
a+1 指向 a[1]
a+2 指向 a[2]
,
a+i 指向 a[i]
,
a+9 指向 a[9]
首页 上页 下页 节 末页 结束
3、指向数组的指针变量也可带下标
如,p[ i] 与 *( p+i) 等价。
若 p=a; 则 p[ i] 与 a[i ] 等价。
总结,用指针访问数组的一般形式
对下标为 i的元素访问,
a[i],*(a+i),*(p+i),p[i]
对 a[i]的地址表示,
&a[i],a+i,p+i,&p[i]
9.3.2 通过指针引用数组元素
首页 上页 下页 节 末页 结束
一般地, p+i 代表 a[i] 的地址,
引用 a[i]的值, 可使用 a[i],?(p+i),?(a+i),p[i]
#include <stdio.h>
main( )
{ int a[4]={1,2,3,4},*p,i;
p=a;
i=0;
do
{ printf("*p=%d,p=%ld\n",*p,p);
p=p+1;
i=i+1; }
} while (i<4);
9.3.2 通过指针引用数组元素
首页 上页 下页 节 末页 结束
例 1,*运算
main()
{int a[10];
int i,*p;
for (p=a;p<a+10;p++)
scanf("%d",p);
for (p=a;p<a+10;p++)
printf("%d",*p);
printf(”\n");
}
例 2,[ ]运算
main()
{int a[10];
int i,*p=a;
for (i=0;i<10;i++)
scanf("%d",&p[i]);
for (i=0;i<10;i++)
printf("%d",p[i]);
printf("\n");
}
带下标的
数组指针
指针变量
控制循环
指针 增量
下标 增量
9.3.2 通过指针引用数组元素
首页 上页 下页 节 末页 结束
引用对比举例
main(){
int a[10],i;
for(i=0;i<10;i++)
a[i]=i;
for(i=0;i<5;i++)
printf("a[%d]=%d\n",i,a[i]);
}
输出数组中的全部元素。(下标法)
9.3.2 通过指针引用数组元素
运行结果,
例 3
首页 上页 下页 节 末页 结束
9.3.2 通过指针引用数组元素
输出数组中的全部元素。
main()
{
int a[10],i;
for(i=0;i<10;i++)
*(a+i)=i;
for(i=0;i<10;i++)
printf("a[%d]=%d\n",i,*(a+i));
}
通过数组名计算
元素的地址,找
出元素的值
通过数组名计算元素的地址,找
出元素的值
例 4
运行结果,
首页 上页 下页 节 末页 结束
9.3.2 通过指针引用数组元素
输出数组中的全部元素
main(){
int a[10],I,*p;
p=a;
for(i=0;i<10;i++)
*(p+i)=i;
for(i=0;i<10;i++)
printf("a[%d]=%d\n",i,*(p+i));
}
例 5
用指针变量指
向元素
用指针变量指
向元素
运行结果,
首页 上页 下页 节 末页 结束
9.3.2 通过指针引用数组元素
main()
{
int a[10],i,*p=a;
for(i=0;i<10;){
*p=i;
printf("a[%d]=%d\n",i++,*p++);
}
}
指针当前所指的
目标
取指针当前指向
的目标,指针再
做移动
例 6
运行结果,
首页 上页 下页 节 末页 结束
9.3.2 通过指针引用数组元素
几个注意的问题,
指针变量可以实现本身的值的改变。如 p++是合法的;
而 a++是错误的。因为 a是数组名,它是数组的首地
址,是常量。
要注意指针变量的当前值。请看下面的程序 。
【 例 9.7】 找出错误。
main(){
int *p,i,a[10];
p=a;
for(i=0;i<10;i++)
*p++=i;
for(i=0;i<10;i++)
printf("a[%d]=%d\n",i,*p++);
}
首页 上页 下页 节 末页 结束
9.3.2 通过指针引用数组元素
本小节提要,
从上例可以看出,虽然定义数组时指定它包含 10个元素
,但指针变量可以指到数组以后的内存单元,系统并
不认为非法。
*p++,由于 ++和 *同优先级,结合方向自右而左,等价
于 *(p++)。
*(p++)与 *(++p)作用不同。若 p的初值为 a,则 *(p++)等价
a[0],*(++p)等价 a[1]。
(*p)++表示 p所指向的 元素值 加 1。
如果 p当前指向 a数组中的第 i个元素,则
*(p--)相当于 a[i--];
*(++p)相当于 a[++i];
*(--p)相当于 a[--i]。
首页 上页 下页 节 末页 结束
9.3.3 数组名作函数参数
以指针作函数参数,可以将变量的地址传递给函
数,用来返回函数处理结果。
指针作为函数参数的传递方式仍然采用,值
传递”方式,实参中存放的指针值(地址)传递
给形参单元,形参与实参分别占用不同的单元。
因指针值是某对象的“地址”,使得 形参与
实参都指向同一对象,若改变了形参所指单元的
内容,则其实参所指内容也同时被改变。(可用
此法返回函数的结果)
首页 上页 下页 节 末页 结束
9.3.3 数组名作函数参数
主函数中,被调函数,
char *p="C language."; void display(char *q)
display(p); {char s[]="Happy New Year!";
printf("%s\n",p); printf("%s\n",q); q=s;
printf("%s\n"q);}
C language,
p q
Happy New Year! s
运行结果,
C language,
Happy New Yeqr!
C language,
编一程序,用以说明形参指针值的改变并不影响
与其对应的实参的指针值。
首页 上页 下页 节 末页 结束
9.3.3 数组名作函数参数
编一程序,用以测试一下形参指针所指向的单
元(变量)中的内容的改变,对与其对应的实
参指针所指向的单元中的内容的影响。
主函数,被调用函数,
int i=100,m=100; change(int n,int *p)
int *ip=&i; n=50;
printf("m=%d,i=%d\n",m,i); *p=50;
change(m,ip);
printf("m=%d,i=%d\n",m,i);
100
m
ip p
50 50
首页 上页 下页 节 末页 结束
9.3.3 数组名作函数参数
输入 a,b,c三个整数,并按由小
到大的顺序显示出来。
主函数,
int a,b,c,*pa,*pb,*pc;
pa=&a,pb=&b,pc=&c;
sort(pa,pb,pc); 函数,sort( )
sort(int *p1,int *p2,int *p3); {
void swap( );
if(*p1>*p2) swap(p1,p2);
if(*p1>*p3) swap(p1,p3);
if(*p2>*p3) swap(p2,p3); }
a b c
pa pb pc p1 p2 p3
p1 p2 tp
void swap(int *p1,int *p2)
{ int tp; tp=*p1;
*p1=*p2; *p2=tp; }
首页 上页 下页 节 末页 结束
9.3.3 数组名作函数参数
当数组名作函数实参时,传递给函数形参的是
该数组的 起始地址 。如此,函数的形参既可以
定义为数组,也可以定义为指针。 可以在函数
体内通过指针存取数组中的元素
1,用数组
constr(char s1[],char s2[],char s3[]);
i=0; j=0
while(s1(i)!=‘\0’){s3[i]=s1[i]; i++;}
while(s2(j)!=‘\0’){s3[i+j]=s2[j]; j++;}
s3[i+j]='\0';
2,用指针
*s1,c r *s2,char * );
while(*s1)*s3++=*s1++;
while(*s2) *s3++=*s2++;
S3=‘\0’;
例,编写一个合并两个字符串的函数
首页 上页 下页 节 末页 结束
9.3.3 数组名作函数参数
1、形参和实参都是数组名。
main()
{int a[10];
……
f(a,10)
……
f(int x[],int n)
{
……
}
}
a和x指的是同一组数组。
首页 上页 下页 节 末页 结束
9.3.3 数组名作函数参数
2、实用数组,形参用指针变量。
main()
{int a[10];
……
f(a,10)
……
f(int *x,int n)
{
……
}
}
首页 上页 下页 节 末页 结束
9.3.3 数组名作函数参数
3、实参、型参都用指针变量。
4、实参为指针变量,型参为数组名。
用实参指针变量改写将 n个整数按相反顺序
存放。

void inv(int *x,int n)
{int *p,m,temp,*i,*j;
m=(n-1)/2;
i=x;j=x+n-1;p=x+m;
for(;i<=p;i++,j--)
{temp=*i;*i=*j;*j=temp;}
return;
}
首页 上页 下页 节 末页 结束
9.3.3 数组名作函数参数
main()
{int i,arr[10]={3,7,9,11,0,6,7,5,4,2},*p;
p=arr;
printf("The original array:\n");
for(i=0;i<10;i++,p++)
printf("%d,",*p);
printf("\n");
p=arr;
inv(p,10);
printf("The array has benn inverted:\n");
for(p=arr;p<arr+10;p++)
printf("%d,",*p);
printf("\n");
}
首页 上页 下页 节 末页 结束
一、字符串的表现形式
C语言中,有两种方式可以实现字符串:字符数组、字符指针。
9,4 字符串与指针
首页 上页 下页 节 末页 结束
9,4 字符串与指针
首页 上页 下页 节 末页 结束
从以上两个例子中,可以看到,
1、字符数组和字符指针的概念不同。
2、字符指针指向字符串,而 C语言中,字符串按数组方式处理,因此,
字符数组和字符指针的访问方式相同。例如,均可以使用 %s格式控制
符进行整体输入输出。但应注意,如果不是字符数组,而是整型、实
型等数字型数组,不能用 %s,只能逐个元素处理。
9,4 字符串与指针
首页 上页 下页 节 末页 结束
[例 ] 将字符串 a复制到字符串 b。
main()
{
char a[] = "I am a boy.“;
char b[20];
int i;
for(i=0; *(a+i) !='\0'; i++)
*(b+i) = *(a+i);
*(b+i) = '\0';
printf("string a is,%s\n",a);
printf("string b is,%s\n",b);
9,4 字符串与指针
首页 上页 下页 节 末页 结束
for(i=0; b[i] !='\0'; i++)
printf("%c",b[i]);
printf("\n");
}
[例 ] 将字符串 a复制到字符串 b。 (用指针处理 )
main()
{
char a[]="I am a boy.",b[20],*p1,*p2;
int i;
p1 = a; p2 = b;
for(;*p1 != '\0'; p1++,p2++)
9,4 字符串与指针
首页 上页 下页 节 末页 结束
*p2 = *p1;
*p2 = '\0';
printf("string a is,%s\n",a);
printf("string b is,%s\n",b);
for(i=0; b[i] !='\0'; i++)
printf("%c",b[i]);
printf("\n");
}
二、字符串指针作函数参数
9,4 字符串与指针
首页 上页 下页 节 末页 结束
将一个字符串从一个函数传递到另一个函数,可以使
用传地址的方式,即用字符数组名或字符指针变量
作参数。有以下四种情况,
实参 形参
数组名 数组名
数组名 字符指针变量
字符指针变量 字符指针变量
字符指针变量 数组名
[例 ] 用函数调用实现字符串的复制。
( 1)用字符数组作参数。
9,4 字符串与指针
首页 上页 下页 节 末页 结束
Void copy_string(char from[],char to[])
{int i=0;
while(from[i] != '\0')
{ to[i] = from[i]; i++; }
to[i] = '\0';
}
main()
{char a[] = "I am a teacher.";
char b[] = "you are a student.";
printf("string_a =%s\n string_b =%s\n",a,b);
copy_string(a,b);
printf("string_a =%s\n string_b=%s\n",a,b);
}
9,4 字符串与指针
首页 上页 下页 节 末页 结束
main()函数可以改写为(使用字符指针),
main()
{char *a = "I am a teacher.";
char *b = "you are a student.";
printf("string_a =%s\n string_b =%s\n",a,b);
copy_string(a,b);
printf("string_a =%s\n string_b =%s\n",a,b);
}
( 2)形参用字符指针 。
9,4 字符串与指针
首页 上页 下页 节 末页 结束
Void copy_string(char *from,char *to)
{
for(; *form != '\0'; from++,to++)
*to = *from;
*to = '\0';
}
main()
{char *a = "I am a teacher.";
char *b = "you are a student.";
printf("string_a =%s\n string_b =%s\n",a,b);
copy_string(a,b);
printf("string_a =%s\n string_b =%s\n",a,b);
}
9,4 字符串与指针
首页 上页 下页 节 末页 结束
函数的指针:函数的入口地址 ( 函数的首地址 ) 。 C语
言规定函数的首地址就是函数名, 所以函数名就是
函数的指针 。
指向函数的指针变量:存放函数入口地址 ( 函数指针 )
的变量, 称为指向函数的指针变量 。 简称函数的指
针变量 。
函数可以通过函数名调用, 也可以通过函数指针调用 。
通过函数指针实现函数调用的步骤,
1,指向函数的指针变量的定义,
类型 ( * 函数指针变量名 ) ();
例如 int (*p)(); 注意,两组括号 ( ) 都不能少 。
int表示被指向的函数的类型, 即被指向的函数的返
回值的类型 。
9,5 指向函数的指针
首页 上页 下页 节 末页 结束
2,指向函 数的指针 变量的 赋值, 指 向某个函 数,
函数指针变量名 =函数名;
3,利用指向函数的指针变量调用函数,
( * 函数指针变量名 ) ( 实参表 )
例,输入 10个数, 求其中的最大值 。
/* 使用函数名调用函数 */
main()
{int i,m,a[10];
for(i=0; i<10; i++)
scanf("%d",&a[i]);
m=max(a); /* 函数调用格式:函数名 (实参表 ) */
printf("max=%d\n",m);
}
9,5 指向函数的指针
首页 上页 下页 节 末页 结束
int max(int *p) /* max在 10个整数中选择最大值
*/
{ int i,t=*p;
for(i=1; i<10; i++)
if(*(p+i)>t)t=*(p+i);
return t;
}
声明函数
/* 使用函数指针变量调用函数 */
9,5 指向函数的指针
首页 上页 下页 节 末页 结束
main()
{int i,m,a[10],max(int *); /* declare
func */
int (*pf)(); /* define func pointer
*/
for(i=0; i<10; i++)
scanf("%d",&a[i]);
pf=max; /* pf->max() */
m=(*pf)(a); /* call max */
printf("max=%d\n",m);
}
声明
函数
指针的定义,
定义函数指针变量 pf
(返回整型数 )
指针的初始化,
函数指针 pf指向
max
指针的引用,
调用函数指针 pf指向的函数 max
首页 上页 下页 节 末页 结束
int max(int *p)
{ int i,t=*p;
for(i=1; i<10; i++)
if(*(p+i)>t)t=*(p+i);
return t;
}
说明,
( 1) 定义函数指针变量时, 两组括号 ( ) 都不能少 。 如果少了前面的一
组括号 =>返回值类型 * 函数名 (); -返回值为地址值 ( 指针 ) 的函数 。
( 2) 函数指针变量的类型是被指向的函数的类型, 即返回值类型 。
( 3) 函数指针的赋值, 只要给出函数名, 不必给出参数 。 ( 不要给出实
参或形参 ) 。
( 4) 用指针变量调用函数时, (* 函数指针 )代替函数名 。 参数表与使用
函数名调用函数一样 。
9,5 指向函数的指针
首页 上页 下页 节 末页 结束
( 5) 可以看出, 定义的函数指针变量可以用于一类函数,
只要这些函数返回值类型 ( 函数类型 ) 相同 。
函数可以通过函数名调用, 也可以通过函数指针调用 。 函
数指针常常用在函数需要作为函数参数的情况 。
用指向函数的指针作为函数的参数 ( 常用于编制, 通用,
的函数 )
函数的参数除了可以是变量, 指向变量的指针, 数组 ( 实
际是指向数组的指针 ), 指向数组的指针以外, 还可以
是函数的指针 。
函数的指针可以作为函数参数, 在函数调用时可以将某个
函数的首地址传递给被调用的函数, 使这个被传递的函
数在被调用的函数中调用 ( 看上去好象是将函数传递给
一个函数 ) 。 函数指针的使用在有些情况下可以增加函
数的通用性, 特别是在可能调用的函数可变的情况下 。
9,5 指向函数的指针
首页 上页 下页 节 末页 结束
例:编制一个对两个整数 a,b的通用处理函数 process,要求根
据调用 process时指出的处理方法计算 a,b两数中的大数,
小数, 和 。
解,int max(int,int );
int min(int,int );
int add(int,int );
int add1(int);
main()
{int a,b;
printf(“enter two num to a,b:”);
scanf("%d%d",&a,&b);
声明 3个处理函数 (只
需要形参类型 )
首页 上页 下页 节 末页 结束
printf("max=%d\n",process(a,b,max)); /* 调用通用处
理函数 */
printf("min=%d\n",process(a,b,min));
printf("add=%d\n",process(a,b,add));
printf("add1=%d\n",process(a,b,add1));
}
int max(int x,int y){ return x>y?x:y; } /* 返回两数
之中较大的数 */
int min(int x,int y){ return x<y?x:y; } /* 返回两数
之中较小的数 */
int add(int x,int y){ return x+y; } /* 返回两数
的和 */
int add1(int x){ return x+1; }
否则编译器不知道 max,min是
变量还是什么其它符号
虚实结合时,
函数指针赋值
首页 上页 下页 节 末页 结束
int process(int x,int y,int (*f)()) /* 通用两数的处理函数 */
{ return (*f)(x,y); }
结果,
Enter two num to a,b:3 8
max=8
min=3
add=11
add1=4
说明,
调用函数指针指
向的函数
首页 上页 下页 节 末页 结束
( 1) 函数 process处理两个整数数, 并返回一个整型值 。 同
时又要求 process具有通用处理能力 ( 处理求大数, 小数,
和 ), 所以可以考虑在调用 process时将相应的处理方法
(, 处理函数, ) 传递给 process。
( 2) process函数要接受函数作为参数, 即 process应该有
一个函数指针作为形式参数, 以接受函数的地址 。 这样
process函数的函数原型应该是,
int process(int x,int y,int (*f)());
( 3), 函数指针作为函数参数, 的使用与 1,4,1节介绍的
步骤完全相同, 即函数指针变量的定义 -在通用函数
process的形参定义部分实现;函数指针变量的赋值 -在
通用函数的调用的虚实结合时实现;用函数指针调用函
数 -在通用函数内部实现 。
( 4) main函数调用通用函数 process处理计算两数中大数的
过程是这样的,
首页 上页 下页 节 末页 结束
l将函数名 max( 实际是函数 max的地址 ) 连同要处理的两个
整数 a,b一起作为 process函数的实参, 调用 process函数 。
lprocess函数接受来自主调函数 main传递过来的参数, 包括
两个整数和函数 max的地址, 函数指针变量 f获得了函数
max的地址 。
l在 process函数的适当位置调用函数指针变量 f指向的函数,
即调用 max函数 。 本例直接调用 max并将值返回 。 这样调用
点就获得了两数大数的结果, 由 main函数 printf函数输出
结果 。
同样, main函数调用通用函数 process处理计算两数小数,
和的过程基本一样 。
首页 上页 下页 节 末页 结束
函数可以返回整型, 实型, 字符型等类型的数据, 还可
以返回地址值 -即返回指针值 。
返回指针值的函数定义:类型名 * 函数名 ( 参数表 )
例如,
int *fun(int x,int y)表示 func是返回整型指针的函
数, 返回的指针值指向一个整型数据 。 该函数还包含
两个整型参数 x,y。
9,6 返回指针值的函数
首页 上页 下页 节 末页 结束
系统内存分配函数 void *malloc(size_t size);也是一个返
回指针值的函数 。 返回的指针指向一片分配好的内存空间 。
例,返回两个数中大数地址的函数 。
int *fun(int,int);
main()
{
int i,j,*p;
printf("enter two num to i,j:");
scanf("%d%d",&i,&j);
p=fun(i,j); /* 调用 fun,返回大数地址,赋值给指针
变量 p */
printf("max=%d\n",*p); /* 打印 p指向的数据 */
}
9,6 返回指针值的函数
首页 上页 下页 节 末页 结束
int *fun(int x,int y) /* fun函数返回形参 x,y中较
大数的地址(指针) */
{
int *z;
if(x>y)z=&x; else z=&y;
return z;
}
结果,
enter two num to i,j:12 38
max=38
9,6 返回指针值的函数
首页 上页 下页 节 末页 结束
说明,
( 1) main函数从键盘获得两个整数 i,j( 本例 12,38) 。 将
i,j作为实参调用 fun。
( 2) 通过虚实结合, fun函数的形参 x,y获得了这两个整数
( 本例 12,38), 将大数的地址返回 ( 本例是 &y) 。
( 3) 返回的地址值赋值给 main函数的指针变量 p,main函数
打印 p指向的整型数, 即 y的值 。
9,6 返回指针值的函数
首页 上页 下页 节 末页 结束
9.7.1指针数组
数组的指针:指向数组元素的指针 。 数组元素可以是
一般数据类型, 也可以是数组, 结构体等数据类型 。
数组指针的定义与数组元素指针的定义相同 。
9,7 指针数组和指向指针的指针
首页 上页 下页 节 末页 结束
指针数组:一个数组, 如果其数组元素均为指针, 那么此
数组为指针数组 。
一维指针数组的定义:类型名 *数组名 [数组长度 ];
例如,int *p[4];
定义一个 4个元素的数组 p,其中每个元素是一个整型指针,
也即数组 p是一个 4元素整型指针数组 。
又如,char *p[4];
定义一个 4个元素的字符指针数组 p,其中每个数组元素是
一个字符指针, 可以指向一个字符串 。 也就是说利用
此字符指针数组可以指向 4个字符串 。
指针数组用得最多的是, 字符型指针数组,, 利用字符指
针数组可以指向多个长度不等的字符串, 使字符串处
理更加方便, 灵活, 节省内存空间 。
9,7 指针数组和指向指针的指针
首页 上页 下页 节 末页 结束
例:将若干字符串按字母顺序由小到大输出 。
说明,
( 1) main()中定义了指针数组 name,它有 4个元素, 其
初值分别是 "C Program","BASIC","Computer
English","Word"四个字符串常量的首地址 。
( 2) 函数 sort使用选择排序法对指针数组指向的字
符串进行排序 ( 按字母顺序 ), 在排序过程中不交换
字符串本身, 只交换指向字符串的指针 ( name[k]<-
>name[i]) 。
9,7 指针数组和指向指针的指针
首页 上页 下页 节 末页 结束
( 3) 排序前后指针数组的指向参看教材
( 4) 利用字符指针数组进行字符串排序 。
9.7.2 指针的指针
1, 指针的指针:指向指针变量的指针变量 。 指针的指
针存放的是指针变量地址,
指针变量的指针变量 ( 指针的指针 ) 的定义,
类型 **指针变量名;
例如,
P2 p1 i &p1 &i
2
9,7 指针数组和指向指针的指针
首页 上页 下页 节 末页 结束
int i=2; /* 定义整型变量 i */
int *p1,**p2; /* 定义 p1为整型指针,定义 p2为整型
指针的指针 */
p1=&i; /* i的地址 =>p1,即,指针 p1指向变量 i */
p2=&p1; /* 指针 p1的地址 =>p2,即,指针 p2指向指针 p1
*/
对变量 i的访问可以是 i,*p1,又因为 *p2=p1,即,
**p2=*p1,所以对变量 i的访问可以是 i,*p1,**p2。
2,指针的指针与指针数组的关系
9,7 指针数组和指向指针的指针
首页 上页 下页 节 末页 结束
我们知道, 数组的指针是指向数组元素的指针 ( 整型, 实
型, 字符型一维数组的指针分别是指向整型, 实型, 字
符型指针, 二维数组的指针是指向一维数组的指针 ) ;
同理,
指针数组的指针, 也是指向其数组元素的指针 。 指针数组
的数组元素是指针, 所以 指向指针数组的指针就是指针
的指针 。 也就是说, 可以使用, 指针的指针, 指向指针
数组 。
例:指向指针的指针变量的应用 。
main()
{char *name[]={"C Program","BASIC","Computer
English","Word"};
char **p;
for(p=name; p<name+4; p++)
printf("%s\n",*p); /* *p=name[i],name[i] is a
address */
}
首页 上页 下页 节 末页 结束
9.7.3 指针数组的应用 -指针数组作为 main()函数的参数 。
1,main()函数可以带参数 。
main()函数是整个可执行程序的入口 ( 执行起点 ) 。 main函
数也与其它函数一样可以带参数, 指针数组的一个重要应
用是作为 main函数的形参 。 人们习惯将 argc,argv作为
main()函数的形参名 。
带参数 main函数的完整的原型是,
类型 main(int argc,char *argv[]);
其中,
(1)argc是传递给 main()函数的参数的个数 。 ( 包括可执行程
序名 )
(2)argv是传递给 main()函数的字符指针数组, 该数组各个元
素是字符指针, 分别指向调用 main()函数时在操作系统命
令行输入的各个字符串 。 ( 包括可执行程序名 )
2,main函数如何获得参数? -从操作系统命令行获得参数 。
首页 上页 下页 节 末页 结束
(1)C语言源程序经过编译, 连接获得一个在操作系统下可以
直接执行的程序 ( 可执行程序 ) 。 操作系统调用可执行程
序的方法是在操作系统命令提示符下输入,
C,> 可执行程序名 (命令名 ) 参数表 <CR> ( 操作系统命
令行 )
(2)操作系统调用可执行程序时, 将操作系统命令行中的
,( 程序 ) 命令名, 以及各个参数 ( 字符串 ) 作为 main()
函数的参数, 传递给 main()函数的 argc,argv。 然后程序
由 main()函数开始运行程序 。
例如,DOS的 xcopy命令 ( 外部命令 ) 实际是一个程序 。
其使用是格式是,xcopy c:\mycprg\*.c d:\mybak /s<CR>
例:编写一个命令文件, 把键入的字符倒序打印出来 。 设文
件名为 invert.c。
9,7 指针数组和指向指针的指针
首页 上页 下页 节 末页 结束
main(int argc,char *argv[])
{ int i;
for(i=argc-1; i>=0; i--)
printf("%s\n",argv[i]);
}
E:\10a>invert I love China
China
love
I
E:\10A\INVERT.EXE
9,7 指针数组和指向指针的指针
首页 上页 下页 节 末页 结束
int i=2; /* 定义整型变量 i */
int *p1,**p2; /* 定义 p1为整型指针,定义 p2为整型
指针的指针 */
p1=&i; /* i的地址 =>p1,即,指针 p1指向变量 i */
p2=&p1; /* 指针 p1的地址 =>p2,即,指针 p2指向指针 p1
*/
对变量 i的访问可以是 i,*p1,又因为 *p2=p1,即,
**p2=*p1,所以对变量 i的访问可以是 i,*p1,**p2。
9,7 指针数组和指向指针的指针
首页 上页 下页 节 末页 结束
指针是 C语言中广泛使用的一种数据类型。运用指针编程是 C
语言最主要的风格之一。利用指针变量可以表示各种数据
结构 ;能很方便地使用数组和字符串 ;并能象汇编语言一样
处理内存地址,从而编出精练而高效的程序。要求掌握,
1、指针变量的概念、定义和引用
2、指向一维数组的指针变量和指向字符串的指针变量
的定义及使用
3、指向数组元素和符串中字符的指针变量的使用
4、指针变量作函数参数与数组名或字符串作函数参数的
关系
本章小结
首页 上页 下页 节 末页 结束
指针是一种 _______
A,标识符
B,变量
C,内存地址
D,运算符
本题考核了指针的一般概念。正确答案是 C。
课堂同步,做做与练练
【 练 9.1】
试题分析
选择正确的选项,
首页 上页 下页 节 末页 结束
为指针变量 p输入整型变量 i的地址,可以使用命令 ______
A,Scanf(“%d”,&p);
B,*p=I;
C,p=&I;
D,*p=&I;
本题考核了指针变量赋值的一般形式。正确答案是 C。
课堂同步,做做与练练
【 练 9.2】
试题分析
选择正确的选项,
首页 上页 下页 节 末页 结束
若有 int(*p)[m];,则 p是 _____
A,m个元素的整型数组
B,指向 m个元素的整型数组的指针
C,m个整型指针的数组
D,指向 m个整型指针数组的指针
本题考核了数组的指针。正确答案是 B。
课堂同步,做做与练练
【 练 9.3】
试题分析
选择正确的选项,
首页 上页 下页 节 末页 结束
若有定义 int *p;float *q,下面操作不正确的是 ____
A,p=(int *)q;
B,q=(float *)p;
C,p=(int)*q;
D,*q=(float)*p;
本题考核了指针一般定义形式。正确答案是 C。
课堂同步,做做与练练
【 练 9.4】
试题分析
选择正确的选项,
首页 上页 下页 节 末页 结束
1、指针变量虽然都是存放指针,但 ______不同
2、空指针名称是 _____,表示没有指针的一种状态
3、不同类型的指针变量的赋值必须 ________
1、基类型
2,NULL
3、显示类型变换
课堂同步,做做与练练
【 练 9.5】
试题答案
填空,
首页 上页 下页 节 末页 结束
课堂同步,课后练习与作业
1、用选择法对 10个整数排序
2、编写一个 strlen函数,它能对一个字符串测出其
长度,函数返回值就是字符串的长度(说明:长度
不包括字符串结束标志,\0”)