第九章:指针
?重点及要求:
?掌握指针的概念、定义、指针变量的引
用,掌握地址与指针的关系,掌握指针
与数组的关系。
?掌握指针与函数的关系。
?熟悉指针的运算,了解指向 void类型的
指针。
指针概述 (1)
一、指针概述:
1,地址的概念与取地址运算,
内存以字节编码,每个编码都是一个地址。我们原先学过的变
量、数组、函数等都放在内存中,至于它们放在内存的什么地方,
这都是机器的事,我们只要知道它们是以怎样的顺序放在内存中
的,以便一一按顺序引用。我们怎样知道机器将某种数据放在内
存的什么地方呢?可用求地址运算符 &
如,int a = 3 ;
&a 就是变量在内存中的地址。
可以用 printf(“%x \n”,&a); 看出其地址。 注意,这个地址并不
是始终不变的,这是由机器和操作系统来安排的,我们无法预先
知道。
指针变量 (1)
则 a表示的地址和 &a [0]的地址相同。
但 &不能施加在常数、常量或表达式上,也不能施加在寄存器
变量上(因为寄存器变量在 cpu中,不在内存中)。
二、指针变量:
既然存储在内存中的各种变量都有一个地址,我们能否这样设
想:定义某种变量,让这个变量的值始终等于某个变量的地址,
如同某个房间号、门牌号一样?回答是肯定的。我们把 这种存放
某种变量地址的变量称为指针变量。
10
35
….
a
b
2010
2012
p2010
q2012
因此,在 C语言中,将地
址形象化地称为 指针
4
指针变量
说明 (系统对变量的访问形式分为两种 )
一个变量的访问(访问是指取出其值或向它赋值)方式
有两种:
( 1)直接访问,通过变量名访问,如通过变量名 i直接
访问。
( 2)间接访问,通过该变量的指针来访问,如通过 指针
p访问变量 i。
5
基类型,即该指针变
量所指向的变量的类

指针变量
三、定义指针变量:
[存储类型 ] 数据类型 * 指针变量名 1,* 指针变量
名 2,… ;
如,int *p,a = 3;
int *q,b=5;
如何使一个指针变量指向另
外一个变量呢?
P=&a;
q=&b;
10
35
….
a
b
2010
2012
p2010
q2012
6
指针变量
? 为什么要为指针变量定义类型:
由于不同的数据有不同的数据类型,如 char
仅占一个字节,int 占两个字节,float型占三
个字节,而内存又是以字节为单位进行地址编
号,因而对 char型,只要地址数加减 1,取出
里面的数据就是 char型的完整数据。对 int型就
不同了,要取出其中的数据需 2字节,就不能
对地址进行简单的加减 1了。怎样取出一个完
整的 int型数据或 float型数据呢?如果能够将指
针变量也定义为具有同样的数据类型,那么对
指针进行加 1或减 1运算,就是让指针移动相应
基类型对应的字节数。
指针变量 (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
8
也可以在定义指针变量的同时指定其初值,如、
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; ( *表示乘法运算符)
指针变量 (3)
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赋值 */
10
例 l6_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);
}
这里有一个运算符 *,其作用是:加在指针变
量的前面,取出该指针变量所指对象的内容。
指针变量 (4)
例 l6_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);
}
交换前
下面表示 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
指针变量 (5)
例 l6_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);
}
指针与数组
一、一维数组的指针表示法(指向数组的指针)
复习:一维数组在内存中的存放方法?
A[0]
A[1]
A[2]
A[3]
A[4]
P=a
p=a+1
P=a+2
P=a+3
P=a+4
例 L6_5.cmain( )
{ int a [5] ={1,3,5,7,9},*p;for(i=0;i<5;i++)
printf(“%d”,*(a+i));
Printf(“\n”);/*地址法 */for (p=a; p<a+5; p++)
printf(“%d”,*p);/*指针法 */
}
如何用一个指针变量将数组中的元素取出并显示出来?
指向数组的指针
当 p=a后,有:
p=a=&a [0] *p=*(a+0)=a [0]
p+1=a+1=&a [1]
*(p+1)=*(a+1)=a [1]
… …
这里 *(p+1)若写成 *p+1就不行了,*p+1的
内容是 2。
16
? 复习:
? 1.指针变量作为函数的参数( p138例题)
? 函数的参数不仅可以是整型、实型等基本数据类型,
还可以是指针类型。它的作用是把地址传给被调函
数。 注意, 被调用函数不能改变实参指针变量的值,
但可以改变实参指针变量所指向的变量的值。
? 几种常见的错误 (p139)
? (1)(2)(3)
? 引用一个数组元素,可以用两种方法:
? 下标法:通过数组元素序号来访问数组元素,用 a[i]
? 形式来表示;
? 指针法:通过数组元素的地址访问数组元素,用
*(p+i)或 *(a+i)的形式来表示。
17
? [例 9.5] 输出数组的全部元素。(设 10个元素,整
型)。
? 访问各元素有三种方法:
? 1、下标法(常用,很直观)
? main()
? {int a[10];
? int i;
? for(i=0;i<10;i++)
? scanf("%d",&a[i]);
? printf("\n");
? for(i=0;i<10;i++)
? printf("%d ",a[i]);
? }
18
? 2、用数组名计算数组元素的地址。(效率与下标法
相同,不常用)
? main()
? {
? int a[10];
? int i;
? for(i=0;i<10;i++)
? scanf("%d",&a[i]);
? printf("\n");
? for(i=0;i<10;i++)
? printf("%d ",*(a+i));
? }
19
? 3、用指针访问各元素。(常用,效率高)
? main()
? {
? int a[10];
? int *p,i;
? for(i=0;i<10;i++)
? scanf("%d",&a[i]);
? printf("\n");
? for(p=a;p<(a+10);p++) /* p++使 p指向下一
个元素 */
? printf("%d ",*p);
? }
20
? 使用指针指向数组,应注意以下问题:
? 1、若指针 p指向数组 a,虽然 p+i与 a+i、
*(p+i)与 *(a+i)意义相同,但仍应注意 p与 a
的区别 (a代表数组的首地址,是不变的; p
是一个指针变量,可以指向数组中的任何元
素 ),例、
? for(p=a; a<(p+10); a++) a代表数组的
首地址,是不变的,a++不合法
? printf("%d",*a)
? 2、指针变量可以指向数组中的任何元素,
注意指针变量的当前值。
21
? [例 9.6] 输出数组 a的 10个元素。
? 程序:
? main()
? {
? int *p,i,a[10];
? p = a;
? for(i=0;i<10;i++)
? scanf("%d",p++);
? printf("\n");
? for(i=0;i<10; i++,p++) ( p的值不对)
? printf("%d",*p);
? }
22
? main()
? {
? int *p,i,a[10];
? p = a;
? for(i=0;i<10;i++)
? scanf("%d",p++);
? printf("\n");
? p = a;
? for(i=0;i<10; i++,p++)
? printf("%d",*p);
? }
? 此程序正确
23
? 3、使用指针时,应特别注意避免指针访问越
界。在上例中,第二次 for循环,p已经越过
数组的范围,但编译器不能发现该问题。避
免指针访问越界是程序员自己的责任。
? 4、指针使用的几个细节。
? 设指针 p指向数组 a( p=a),则:
? ① p++(或 p += 1),p指向下一个元素。
? ② *p++,相当于 *(p++)。因为,*和 ++同优
先级,++是右结合运算符。
? ③ *( p++)与 *(++p)的作用不同。
? *(p++):先取 *p,再使 p加 1。
? *(++p):先使 p加 1,再取 *p。
24
? ④ (*p)++表示,p指向的元素值加 1。
? ⑤ 如果 p当前指向数组 a的第 i个元素,则:
? *(p--)相当于 a[i--],先取 *p,再使 p减 1。
? *(++p)相当于 a[++i],先使 p加 1,再取 *p。
? *(--p)相当于 a[--i],先使 p减 1,再取 *p。
? 数组名作函数参数
? 数组名代表数组首地址,因此,它作实参在
函数调用时,是把数组首地址传送给形参。
这样,实参数组和形参数组共占同一段内存
区域。从而在函数调用后,实参数组的元素
值可能会发生变化。
25
? [例 9.7] 将数组 a中 n个元素按相反顺序存放。
? 算法,a[0]与 a[n-1]交换,a[1]与 a[n-2]交
换,.....,a[(n-1)/2]与 a[n-int((n-
1)/2)]交换。
? 实现:用 i,j作元素位置变量,开始
i=0,j=n-1。将 a[i]与 a[j]交换,然后 i加 1,
j减 1,直到 i=(n-1)/2。
26
? 程序:
? Void inv(int x[],int n) /* 形参是数组 */
? {int t,i,j,m=(n-1)/2;
? for(i=0; i<=m; i++)
? { j = n - 1 - i;
? t = a[i]; a[i] = a[j]; a[j] = t;
? }
? return; /* 函数的返回值类型是 void,不返回任
何值 */
? }
? main()
? {
27
? static int i,a[10] = {3,7,9,11,0,6,7,5,4,2};
? printf("the original array:\n");
? for(i=0; i<10; i++)
? printf("%d ",a[i]);
? printf("\n");
? inv(a,10);
? printf("the array has been inverted:\n");
? for(i=0; i<10; i++)
? printf("%d ",a[i]);
? printf("\n");
? }
?
28
? 函数 inv()可以用指针作形参,运行情况与用数组
作形参相同。
? void inv(int *x,int n)
? {int *p,t,*i,*j,m=(n-1)/2;
? i = x; /* 指针 i指向数组第一个元素 */
? j = x + n - 1;/* 指针 j指向数组最后一个元素
*/
? p = x + m; /* 指针 p指向数组中间一个元素 */
? for(; i<=p; i++,j--)
? { t = *i; *i = *j; *j = t; }
? }
?
29
? 小结:数组作函数的参数,实参和形参之间
传送数组的首地址,首地址可以用指针表示,
也可以用数组名表示。
二维数组的指针
若再定义 int *p;
p=&a [0] [0];
这时 p+1=? &a [0] [1]
p+3=? &a [1] [0]
p+6=&a [2] [0]
A[0]表示第 0行 ….
A[i]表示第 i行
因此 a[0]代表第 0行的首地址,它
与 &a[0][0]等价。
例 L6_6.c
A[0][0]
A[0][1]
A[0][2]
A[1][0]
A[1][1]
A[1][2]
A[2][0]
A[2][1]
A[2][2]
P=&A[0][0]
P+1
P+2
:
P+i
:
P+8
二、二维数组的指针表示法
复习二维数组在内存中的表示法?
二维数组的指针表示法
main( )
{ int a [3] [3]={1,2,3,4,5,6,7,8,9};
int *p;
for (p= a[0] ; p<a[0]+10 ; p++)
printf(,%d,,*p);
printf(,\n,);
}
从中可看出,这样用 p指针表示二维数组很不直观,指向
时不知指在哪一行、哪一列。如果我们这样来理解二
维数组,把二维数组看作是由若干行向量 (一维数组)
组成的:如下图所示:
每一行的元素
个数
二维数组的指针表示法
定义:
[存储类型 ] [数据类型 ] ( *指针变量 名) [元素个数 ]
例如,int a[3][4];
int (*p)[4]; p=a[0];
A[0] 1 2 3
A[1] 4 5 6
A[2] 7 8 9
P
P+1
P+2
所以,我们可以定义一种指向行向量的指针,让它指向二
维数组的第一行,该指针加 1,就指向二维数组的下一行,
这样的指针就叫做 行指针。
二维数组的指针表示法
通过上一节的讨论,我们可以得出两点接结论:
1、在 C语言中,一维数组名代表了该数组的起始地
址,或者说,它是一个指向所代表数组,即指向其
起始单元的指针。
2、在 C语言中,一维数组的任何一个元素的地址,
都可以用其数组名加上一个偏移量来表示,这个位
移量的单位不是字节,而是数组元素的大小。
这两点可以推广到二维 乃至多维数组。
二维数组 a[m][n]可以看做是由 m个一维数组 a[0]、
a[1],……a[m -1]组成。由此我们可以推导,用指
向一维数组的指针(行指针)来表示二维数组元素:
A[0][0]
A[0][1]
A[0][2]
A[1][0]
A[1][1]
……
A[i][0]
……
A[i][j]
A[2][2]
A[0]
P
P+1
P+i
行向量地址
*(p+0)+0
*(p+0)+1
*(p+0)+2
*(p+1)+0
*(p+1)+1
…….
*(p+i)+0
*(p+i)+j
*(p+2)+2
元素地址
*(*(p+0)+0)
*(*(p+0)+1)
*(*(p+0)+2)
*(*(p+1)+0)
*(*(p+1)+1)
…….
*(*(p+i)+0)
*(*(p+i)+j)
*(*(p+2)+2)
元素值
*p[0]
*(p[0]+1
)
*(p[0]+2
)
*(p[1])
*(p[1]+1
)
*p[i]
*(p[i]+j)
*(p[2]+2
p
二维数组的指针表示法
例 L6_7.c
main( )
{ int a [3] [5]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
int j,k,(*p) [5];
p=a;
for (j=0; j<3; j++)
for (k=0; k<5; k++)
printf(,% d,,*(*(p+j)+k));
printf(,\n,);
}
P实际上是一个二级指针,必须对它施加两次 *运算,才能得
到相应的数组元素值。
36
字符串的指针
一、字符串的表现形式
C语言中,有两种方式可以实现字符串:字符数组、字符指针。
37
38
? 从以上两个例子中,可以看到:
1、字符数组和字符指针的概念不同。
2、字符指针指向字符串,而 C语言中,字符串按数组方式处
理,因此,字符数组和字符指针的访问方式相同。例如,
均可以使用 %s格式控制符进行整体输入输出。但应注意,
如果不是字符数组,而是整型、实型等数字型数组,不能
用 %s,只能逐个元素处理。
39
[例 ] 将字符串 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);
40
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++)
41
*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");
}
二、字符串指针作函数参数
42
? 将一个字符串从一个函数传递到另一个函数,
可以使用传地址的方式,即用字符数组名或
字符指针变量作参数。有以下四种情况:
? 实参 形参
? 数组名 数组名
? 数组名 字符指针变量
? 字符指针变量 字符指针变量
? 字符指针变量 数组名
? [例 9.20] 用函数调用实现字符串的复制。
? ( 1)用字符数组作参数。
43
? 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);
? }
44
? 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)形参用字符指针 。
45
? 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);
? }
46
? 三、字符串指针变量与字符数组的区别
? 字符数组 字符指针变量
? 由若干元素组成,每个元素中放一个字符。 存放字符串的首地址。
? static char str[]={"I love China!"}; char *a = "I love China!";
? char str[14]; char * a;
? str = "I love China!" a = "I love China!";
? 字符数组一个元素占一字节内存,且在编译时分配 。 指针变量中只可以放一个地址值(近指针 =2字节,
远指针 =4 字节)。且编译时未指定
? char str[10]; char *a;
? scanf(“%s”,str); scanf(“%s”,a);尚未指向任何变量
? 字符数组名是地址常量 指针变量可以进行++、--等运算
? 不允许进行++、--等运算 指针的变量的值允许改变
? 书上例题( p154,155)
? 10,4 指向函数的指针变量
? 10,4,1 函数的指针, 使用函数指针调用函数 。
47
指向函数的指针变量
? 函数的指针:函数的入口地址 ( 函数的首地址 ) 。 C
语言规定函数的首地址就是函数名, 所以函数名就是
函数的指针 。
? 指向函数的指针变量:存放函数入口地址 ( 函数指针 )
的变量, 称为指向函数的指针变量 。 简称函数的指针
变量 。
? 函数可以通过函数名调用, 也可以通过函数指针调用 。
? 通过函数指针实现函数调用的步骤:
1,指向函数的指针变量的定义,
类型 ( * 函数指针变量名 ) ();
例如 int (*p)(); 注意,两组括号 ( ) 都不能少 。 int表示
被指向的函数的类型, 即被指向的函数的返回值的类型 。
48
? 2,指向函数的指针变量的赋值, 指向某个函数:
函数指针变量名 =函数名;
? 3,利用指向函数的指针变量调用函数:
? ( * 函数指针变量名 ) ( 实参表 )
? 例 10-14:输入 10个数, 求其中的最大值 。 P155.
? /* 使用函数名调用函数 */
? main()
? {int i,m,a[10];
for(i=0; i<10; i++)
? scanf("%d",&a[i]);
m=max(a); /* 函数调用格式:函数名 (实参表 )
*/
printf("max=%d\n",m);
? }
49
? 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;
? }
? 结果:
? -52 87 29 79 -32 94 23 -112 46 67
? max=94
? 声明函数
? /* 使用函数指针变量调用函数 */
50
? 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
51
? 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) 用指针变量调用函数时, (* 函数指针 )代替函数名 。
参数表与使用函数名调用函数一样 。
52
? ( 5) 可以看出, 定义的函数指针变量可以用于一类函数,
只要这些函数返回值类型 ( 函数类型 ) 相同 。
? 函数可以通过函数名调用, 也可以通过函数指针调用 。 函数
指针常常用在函数需要作为函数参数的情况 。
? 10,4,2 用指向函数的指针作为函数的参数 ( 常用于编制
,通用, 的函数 )
? 函数的参数除了可以是变量, 指向变量的指针, 数组 ( 实际
是指向数组的指针 ), 指向数组的指针以外, 还可以是函数
的指针 。
? 函数的指针可以作为函数参数, 在函数调用时可以将某个函
数的首地址传递给被调用的函数, 使这个被传递的函数在被
调用的函数中调用 ( 看上去好象是将函数传递给一个函数 ) 。
函数指针的使用在有些情况下可以增加函数的通用性, 特别
是在可能调用的函数可变的情况下 。
?
53
? 例 10-15:编制一个对两个整数 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个处理函数 (只
需要形参类型 )
54
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是
变量还是什么其它符号
虚实结合时,
函数指针赋值
55
? 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
? 说明:
定义函数
指针变量调用函数指 针指向的函数
56
? ( 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处理计算两数
中大数的过程是这样的:
57
?l将函数名 max( 实际是函数 max的地址 ) 连同要处
理的两个整数 a,b一起作为 process函数的实参, 调
用 process函数 。
?lprocess函数接受来自主调函数 main传递过来的参
数, 包括两个整数和函数 max的地址, 函数指针变
量 f获得了函数 max的地址 。
?l在 process函数的适当位置调用函数指针变量 f指
向的函数, 即调用 max函数 。 本例直接调用 max并将
值返回 。 这样调用点就获得了两数大数的结果, 由
main函数 printf函数输出结果 。
? 同样, main函数调用通用函数 process处理计算两
数小数, 和的过程基本一样 。
?
58
? ( 5) 教材,P158,process函数头部:函数指针定
义中不需要指定形参个数 。 但是一般情况函数指针
指向的函数参数个数一般是数量类型相同的, 以便
用统一的格式如 (*f)(x,y)去调用 。
? process函数是一个, 通用, 整数处理函数, 它使
用函数指针作为其中的一个参数, 以实现同一个函
数中调用不同的处理函数 。
? 10,5 返回指针值的函数
? 函数可以返回整型, 实型, 字符型等类型的数据,
还可以返回地址值 -即返回指针值 。
? 返回指针值的函数定义:类型名 * 函数名 ( 参数表 )
? 例如:
? int *fun(int x,int y)表示 func是返回整型指针的
函数, 返回的指针值指向一个整型数据 。 该函数还
包含两个整型参数 x,y。
59
? 系统内存分配函数 void *malloc(size_t size);也
是一个返回指针值的函数 。 返回的指针指向一片分
配好的内存空间 。
? 例 10-16:返回两个数中大数地址的函数 。
? 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指向的数据 */
? }
60
? 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
61
? 说明:
? ( 1) main函数从键盘获得两个整数 i,j( 本例 12,
38) 。 将 i,j作为实参调用 fun。
? ( 2) 通过虚实结合, fun函数的形参 x,y获得了这
两个整数 ( 本例 12,38), 将大数的地址返回 ( 本
例是 &y) 。
? ( 3) 返回的地址值赋值给 main函数的指针变量
p,main函数打印 p指向的整型数, 即 y的值 。
? 10,6 指针数组与指向指针的指针
? 10,6,1指针数组
? 数组的指针:指向数组元素的指针 。 数组元素可以
是一般数据类型, 也可以是数组, 结构体等数据类
型 。 数组指针的定义与数组元素指针的定义相同 。
62
? 指针数组:一个数组, 如果其数组元素均为指针,
那么此数组为指针数组 。
? 一维指针数组的定义:类型名 *数组名 [数组长度 ];
? 例如,int *p[4];
? 定义一个 4个元素的数组 p,其中每个元素是一个整
型指针, 也即数组 p是一个 4元素整型指针数组 。
? 又如,char *p[4];
? 定义一个 4个元素的字符指针数组 p,其中每个数组
元素是一个字符指针, 可以指向一个字符串 。 也就
是说利用此字符指针数组可以指向 4个字符串 。
? 指针数组用得最多的是, 字符型指针数组,, 利用
字符指针数组可以指向多个长度不等的字符串, 使
字符串处理更加方便, 灵活, 节省内存空间 。
?
63
? Name
?
? 使用二维字符数组存储多个字符串 (上图 )
? 指针数组 name 字符串
C P r o g r a m \0
B A S I C \0
C o m p u t e r E n g l i s h \0
W o r d
name[0]
name[1]
name[2]
name[3]
C Program
BASIC
Computer English
Word
64
? 使用字符型指针数组指向多个字符串 (上页下图)
? 使用字符型指针数组指向多个字符串与使用两维字符数组存
储多个字符串的比较:
? ( 1) 节省存储空间 (二维数组要按最长的串开辟存储空间 )
? ( 2) 便于对多个字符串进行处理, 节省处理时间 。 ( 使用指
针数组排序各个串不必移动字符数据, 只要改变指针指向的
地址 )
? 例 10-17:将若干字符串按字母顺序由小到大输出 。 P161.
? 说明:
? ( 1) main()中定义了指针数组 name,它有 4个元素, 其
初值分别是 "C Program","BASIC","Computer
English","Word"四个字符串常量的首地址 。
? ( 2) 函数 sort使用选择排序法对指针数组指向的字符串
进行排序 ( 按字母顺序 ), 在排序过程中不交换字符串本身,
只交换指向字符串的指针 ( name[k]<->name[i]) 。
65
? ( 3) 排序前后指针数组的指向参看教材 p161图 10-
12,p162图 10-13。
? ( 4) 利用字符指针数组进行字符串排序 。
? 10,6,2 指针的指针
? 1, 指针的指针:指向指针变量的指针变量 。 指针
的指针存放的是指针变量地址,
? 指针变量的指针变量 ( 指针的指针 ) 的定义:
? 类型 **指针变量名;
? 例如:
P2 p1 i
&p1 &i 2
66
? 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,指针的指针与指针数组的关系
67
? 我们知道, 数组的指针是指向数组元素的指针 ( 整型, 实型,
字符型一维数组的指针分别是指向整型, 实型, 字符型指针,
二维数组的指针是指向一维数组的指针 ) ;同理:
? 指针数组的指针, 也是指向其数组元素的指针 。 指针数组的
数组元素是指针, 所以 指向指针数组的指针就是指针的指针 。
也就是说, 可以使用, 指针的指针, 指向指针数组 。
? 例 10-18:指向指针的指针变量的应用 。 P163.
? 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 */
? }
68
? 10,6,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函数如何获得参数? -从操作系统命令行获得参数 。
69
? (1)C语言源程序经过编译, 连接获得一个在操作系统下可以
直接执行的程序 ( 可执行程序 ) 。 操作系统调用可执行程序
的方法是在操作系统命令提示符下输入:
? C,> 可执行程序名 (命令名 ) 参数表 <CR> ( 操作系统命
令行 )
? (2)操作系统调用可执行程序时, 将操作系统命令行中的
,( 程序 ) 命令名, 以及各个参数 ( 字符串 ) 作为 main()函
数的参数, 传递给 main()函数的 argc,argv。 然后程序由
main()函数开始运行程序 。
? 例如,DOS的 xcopy命令 ( 外部命令 ) 实际是一个程序 。
? 其使用是格式是,xcopy c:\mycprg\*.c d:\mybak /s<CR>
? 例 10-19:编写一个命令文件, 把键入的字符倒序打印出来 。
设文件名为 invert.c。
70
? 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
71
? 10,7 指针运算举例
? 例 10-20:编写函数 length(char *s),函数
返回指针 s所指字符串的长度 。 P164.
? 例 10-21:已知存放在数组 a中的数不相重复,
在数组 a中查找与值 x相等的位置 。 若找到,
输出该值和该值在数组 a中的位置;若没有找
到, 输出相应的信息 。
? P164.