9.1 地址、指针和变量 9.2 指针运算 9.3 指针与数组 9.4 函数与指针 9.5 程序综合举例 9.6 上机实训
【本章要点】 本章主要介绍指针的基本概概念,指针运算,指针与数组,指针与函数等内容。指针是C语言的一个重要概念,也是C语言的一个重要特色。正确灵活的运用指针,可以使程序简洁、紧凑、高效;可以有效地表示复杂的数据结构。可以说,不掌握指针就没有掌握C语言的精华。
9.1 地址、指针和变量 地址:内存单元的编号也叫做地址。 指针:内存单元地址称为指针,即指针就是地址。 9.1 地址、指针和变量 地址:内存单元的编号也叫做地址。 指针:内存单元地址称为指针,即指针就是地址。 指针变量:存放指针的变量称为指针变量。 各种类型变量都有地址,通过地址(指针)或变量名访问变量。
9.1.1地址和指针的基本概念 变量:命名的内存空间,用于存放各种类型的数据。 变量名:变量名是给内存空间取的一个容易记忆的名字。 变量的地址:变量所使用的内存空间的地址。 变量值:在变量单元中存放的数值。即变量的内容。 “&”符号为取地址符号,可以取得变量的地址。
例如:输出整形变量a,b,c在内存分配的地址。 void main() { int a,b,c; printf(“\n%ld ,%ld ,%ld”,&a,&b,&c); } 运行结果为: 393176 , 589831, 687020
直接访问:在程序中使用变量名(变量地址)存取变量值的方式,称为“直接访问”方式。以前的程序中都是采用这种方式。 间接访问:定义一个指针变量来存放了一个变量的地址,通过指针变量存取变量的值,称为“间接访问”方式。 如图9.2所示:指针变量P存放变量a变量的地址393176,a变量的值为5。
9.1.2 指针变量类型的定义 定义的一般格式为: <存储类型> <数据类型> <*指针变量名>[=初始地址值]; 其中: (1)存储类型是任选项,主要有 auto型、register型、static型和extern型四种。 (2)数据类型是指针变量所指向变量数据类型。可以是int、char、float等基本类型,也可是数组等构造类型。 例如: int *p1,*p2;
9.1.3 指针变量的赋值 指针变量一定要周赋值后才可以使用。禁止使用未初始化或未赋值的指针(否则可能导致系统的崩溃)。 指针运算符: (1) &: 取地址运算符。 功能:取变量的地址。单目运算符,结合性为自右向左。 例如:&a为取变量a的地址。 (2) *: 取内容运算符 它的作用:与&相反,表示指针所指向的变量(取变量内容)。*是单目运算符,其结合性为自右向左。 例如:*p为指针变量p所指向的变量。
指针变量的赋值方法: 1.将地址直接赋值给指针变量 例如:float *f=(float *)malloc(4); 注: malloc()函数申请动态分配内存空间,返回空间首地址; free()函数释放不用内存空间。 使用这2个函数时需要声明头文件<stdlib.h>或<alloc.h>。 2.将变量的地址赋值给指针变量 例如: int i,*p; p=&i;
【例9.1】 通过指针变量访问整形变量。 void main() { int a,b; int *p1,*p2; /*定义两个指针变量*/ a=100;b=50; p1=&a; p2=&b; /* p1指向a,p2指向b*/ printf(“%d,%d\n”,a,b); printf(“%d,%d\n”,*p1,*p2); } 运行结果: 100,50
【例9.2】 输入两个整数,按先大后小的顺序输出。 【例9.2】 输入两个整数,按先大后小的顺序输出。 程序如下: void main() { int a,b,*p1,*p2,*p; scanf(“%d%d”,&a,&b); p1=&a;p2=&b; if (a<b) {p=p1;p1=p2;p2=p;} printf(“\na=%d,b=%d\n”,a,b); printf(“max=%d,min=%d\n”,*p1,*p2); } 运行结果为: 输入:5 9 ↙ 输出:a=5,b=9 max=9,min=5 程序运行中,指针变量p1,p2的指向变化如图
9.2 指针运算 9.2.1指针运算符 1.取地址运算符 & 功能:取变量的地址。单目运算符,结合性为自右向左。 例如:&a为取变量a的地址。 2.取内容运算符 * 功能:与&相反,表示指针所指向的变量(取指针内容,即变量)。*是单目运算符,其结合性为自右向左。在*运算符之后跟的变量必须是指针变量。
9.2.2指针变量的运算 1.赋值运算 有以下几种形式: (1) 指针变量初始化赋值 例如: float *f=(float *)malloc(4); int i,*p=&i; (2) 变量的地址赋予指针变量 int a,*p; p=&a; /*把整型变量a的地址赋予整型指针变量p*/ (3) 指针变量的值赋予另一个指针变量(类型相同) int a,*pa=&a,*pb; pb=pa; /*把a的地址赋予指针变量pb*/
或写为:p=&a[0]; 或:int a[5],*p=a; (5) 字符串首地址赋予指针变量。 例如:char *pc; (4) 数组的首地址赋予指针变量。 例如:int a[5],*p; p=a; 或写为:p=&a[0]; 或:int a[5],*p=a; (5) 字符串首地址赋予指针变量。 例如:char *pc; pc="Hello"; 或写为: char *pc="Hello"; 说明:并不是把整个字符串装入指针变量,而是把存放该字符串的首地址装入指针变量。 (6) 函数的入口地址赋予指针变量。 例如: int (*pf)(); pf=f; /*f为函数名*/
2.加减运算 (1)指针加减任意整数运算 运算条件:必须是指向数组的指针变量(即数组指针,在9.3.1节中介绍),才能加可以加上或减去一个整数n。对指向其它类型变量的指针变量作加减运算是毫无意义的。 运算形式:设有数组指针变量p,则有: p+n,p-n,p++,++p,p--,--p 运算都是合法的。 运算作用:指针变量±n就是把指针指向的当前位置向前(减)或向后(加)移动n个元素位置。 注意:数组指针变量±n不是地址加减n,而是移动n个元素位置。 例如: int a[5],*p; p=a; /*p指向数组a,也是指向a[0]*/ p=p+2; /*p指向a[2],即p的值为&p[2]*/
(2)两指针变量相减 运算条件:必须是指向同一数组的2个指针变量才能进行两指针相减运算。 运算形式:设有数组指针变量p、q,则有: p-q 运算作用:相减所得之差是两个指针所指数组元素之间相差的元素个数。 例如: 设pf1=2010H,pf2=2000H且指向同一实型数组a的两个指针变量, 则得: pf1-pf2=(2000H-2010H)/4=4 注意: pf1+pf2是什么意思呢? 毫无实际意义。
(3)指针的关系运算 运算条件:必须是指向同一数组的2个指针变量才能进行关系运算。 运算作用:两个指针变量通过关系运算进行比较,主要用于判断他们的前后位置关系。 例如:设如果pa和qa都指向同一个数组a, 则有:pa>qa、pa<qa、 pa>=qa、 pa<=qa、 pa==qa、 pa!=qa 说明:当pa所指元素在qa之前,则表达式“pa<qa”为真(值为1); 当pa和qa都指向同一个元素时,表达式“pa==qa” 为真(值为1)。
【例9.4】(1)已知指针p的指向如图9.5所示,执行语句*p++;后,*p的值是多少?p指向哪一个元素? 解答: 先进行*p的操作,得到p所指变量的值20,然后进行p+1操作,指向a[2]。 先将指针p加一,指向a[2],然后进行*p操作,值是30。 ++*p是使p所指的变量的内容加一,值为21,p仍指向a[1]。
9.3 指针与数组 9.3.1 数组指针 1.指向一维数组的指针 数组名代表数组的首地址,是一个常量。 数组指针:就是指向数组元素地址的指针变量。 引用数组元素除了用下标法外也可以用指针法来引用数组元素。 例如:int a[8]={2,4,6,8,10,12,14,16}; int *p;p=&a[0]; 如图9.6所示: 下面的语句是等价的: p=&a[0]; p=a;
例如: int a[8]={2,4,6,8,10,12,14,16}; int *p=&a[0]; 它相当于: int *p; p=&a[0]; 有以下四种方法引用数组元素a[k]: *(p+k)、*(a+k)、p[k]、a[k] 前两种称为指针法,后两种称为下标法。 注意:p++; 操作时允许的。 a++; 操作是错误的,因为a是常量。
① 执行p++ ,p指向下一个元素,即a[1]。*p为a[1]的值。 ② *p++,相当于 *(p++)。 注意指针变量的运算。 设指针p=a(a为数组名) ① 执行p++ ,p指向下一个元素,即a[1]。*p为a[1]的值。 ② *p++,相当于 *(p++)。 ③ *(p++)与 *(++p)作用不同。 ④ (*p)++,表示将p所指向的元素值加1. ⑤ 如果p当前指向a数组中的第i个元素,则: *(p--)相当于a[i--],先取p值作“*”运算,再使p自减。 *(++p)相当于a[++i],先使p自加,再作*运算。 *(--p)相当于a[--i], 先使p自减,再作*运算。
【例9.5】用上面介绍的四种不同方法访问数组元素。 #include<stdio.h> void main() { int a[5]={0,1,2,3,4}; int *p,i; for(i=0;i<5;i++) { printf(“%d ”,a[i]); printf(“%d ”,*(a+i)); printf(“%d ”,p[i]); printf(“%d ”,*(p+i)); printf(“\n”); } 程序中的四个输出是一样的。程序输出如下: 0 0 0 0 1 1 1 1 2 2 2 2 3 3 3 3 4 4 4 4
【例9.6】通过指针变量输入输出a数组的8个元素。程序如下: void main() { int *p,i,a[8]; p=a; for (i=0;i<8;i++) scanf(“%d”,p++); printf(”\n”); p=a; /*注意此语句的作用*/ for (i=0;i<8;i++,p++) printf(“%4d”,*p); }
二维数组a,可视为三个一维数组:a[0]、a[1]、a[2];而每个一维数组又是一维数组,分别由4个元素组成,即: 二维数组名 一维数组名 2.指向二维数组的指针 设有一个二维数组a定义为: int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}}; 二维数组a,可视为三个一维数组:a[0]、a[1]、a[2];而每个一维数组又是一维数组,分别由4个元素组成,即: 二维数组名 一维数组名 数组元素 a a[0] a[1] a[2] a[0][0] a[0][1] a[0][2] a[0][3] a[1][0] a[1][1] a[1][2] a[1][3] a[2][0] a[2][1] a[2][2] a[2][3]
用数组名表示二维数组的行地址 数组名总是代表数组的首地址。 则有:a 为二维整型数组名,a=2000。 a 就是第0行的首地址2000。即为a[0] a+1 代表第一行首地址。为2008。即为a[1] a+2 代表第二行首地址。即2016。即为a[2] (2)用数组名表示二维数组元素地址 二维数组中a[0]、a[1]、a[2]都是地址, 则有:*(a+0)、*(a+1)和*(a+2)也是地址,它们分别是第0行、1行和2行的第0列首地址。 因此,a[0]+1等介于*(a+0)+1,即&a[0][1]。 a[1]+2等介于*(a+1)+2,即&a[1][2]。 a[2]+3等介于*(a+1)+3,即&a[1][3]。
a[0][1]的值 可表示为:*(a[0]+1)和*(*(a+0)+1) a[i][j]的值 可表示应为:*(a[i]+j)和*(*(a+i)+j)。 数组元素三种形式引用: ⑴ a[i][j] 下标法 ⑵ *(a[i]+j) 用一维数组名 ⑶ *(*(a+i)+j) 用二维数组名
3.用指针变量指向二维数组及其元素 (1) 指向数组元素的指针变量 二维数组的每个元素在内存中存储在地址连续的存储空间中。如图9.7和9.8所示。用指向数组元素的指针变量来引用数组。
【例9.7】 用指针变量输出二维数组每个元素的值。 void main() { int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}}; int *p; for (p=a[0];p<a[0]+12;p++) printf(“%4d”,*p); } 注意:for语句中的p=a[0]可改为p=&a[0][0],但不能为p=a。
【例9.8】 输出二维数组a中任一行任一列元素的值。 程序如下: void main() { int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}}; int *p=a[0],i,j; scanf(“%d,%d”,&i,&j); printf(“\n%4d”,*(p+i*4+j)); } 运行程序输入:2,2↙ 输出: 21
(2) 指向由m个整数组成的一维数组的指针变量 定义格式: (*标识符)[一维数组元素个数]; 例如:int (*q)[4]; 定义一个指针变量q,它指向包含有4个元素的一维数组。 注意:*q必须放在括弧内,否则就变成了定义指针数组。 由于q是指向有4个整形元素的一维数组的指针变量,因此,q+1是将地址值加上4*2,即指向下一个一维数组。如图9-8所示。
设有如下定义: int a[3][4]= {{1,2,3,4},{5,6,7,8},{9,10,11,12}}; int (*q)[4]; q=a; 则: q+0 为二维数组第0行首地址,与a+0或*(a+0)相同; q+1 为二维数组第1行首地址,与a+1或*(a+1)相同; q+2 为二维数组第2行首地址,与a+2或*(a+2)相同; *(q+i)+j 为第i行第j列元素的地址,与*(a+i)+j相同; *(*(q+i)+j) 为第i行第j列元素,与*(*(a+i)+j)相同,即a[i][j]。
【例9.9】 用指向有m个元素一维数组的指针变量输出二维数组任一行任一列元素的值。 void main() { int a[3][4]= {{1,2,3,4},{5,6,7,8},{9,10,11,12}}; int (*q)[4]; /*指针变量p是指向有4个元素的一维数组*/ q=a; scanf(“%d,%d”,&i,&j); printf(“\na[%d][%d]=%d”,i,j,*(*(q+i)+j)); } 运行结果如下: 输入:1,2↙ 输出:a[1][2]=13
【例9.10】有一个3×3的矩阵a。编程对它进行转置操作。 程序思想:主对线为界,对矩阵元素的行列元纯洁互换即可。即a[i][j]与为a[j][i]交换。 程序结构分为以下几块: (1)对数组a进行初始化; (2)给数组a的指针p赋值; (3)进行数组元素的转置; (4)输出结果数组。
#include<stdio.h> void main() { int a[3][3]={1,2,3,4,5,6,7,8,9}; int i,j,k,(*p)[3]=a; for(i=0;i<3;i++) { for(j=i;j<3;j++) { k=*(*(p+i)+j); *(*(p+i)+j)= *(*(p+j)+i); *(*(p+j)+i)=k; } { printf(“\n”); for(j=0;j<3;j++) printf(“%d ”, *(*(p+i)+j)); 输出结果为: 1 4 7 2 5 8 3 6 9
函数的参数传递可以采用地址传递。所以指针可作为函数参数。 4.指针作为函数的参数 函数的参数传递可以采用地址传递。所以指针可作为函数参数。 当指针作为函数的形参时,实参传递的是地址,在函数中通过地址访问实参,所以,在函数中通过地址对实参的修改影响到实参的值。 【例9.11】输入的两个整数按大小顺序输出。 #include<stdio.h> void swap(int x,int y) { int temp; temp=x; x=y; y=temp; }
void main() { int a,b; scanf("%d,%d",&a,&b); if(a<b) swap(a,b); printf("\n%d,%d\n",a,b); } 运行结果: 1, 2↙ 1,2 可见:此程序没能够达到题目要求。 虽然a<b,并且进入swap()函数进行数据交换,实现了x和y的交换,返回到主函数后,x和y都被释放掉了。
#include<stdio.h> swap(int *p1,int *p2) { int temp; temp=*p1; 【例9.12】输入的两个整数按大小顺序输出。 #include<stdio.h> swap(int *p1,int *p2) { int temp; temp=*p1; *p1=*p2; *p2=temp; } void main() { int a,b; int *p1,*p2; scanf("%d,%d",&a,&b); p1=&a;p2=&b; if(a<b) swap(p1,p2); printf("\n%d,%d\n",a,b); 运行结果: 1, 2↙ 2,1
5.数组名作为函数参数 【例9.13】 在主函数中定义数组,在被调用函数中用库函数产生20个100以内的随机整数,在主函数中输出这些整数。 程序如下: #include<stdio.h> #include “stdlib.h” #define N 20 getin(int x[ ],int m) { int i; for (i=0;i<m;i++) x[i]=random(100); /*产生100以内的随机整数*/ }
{ if (i%5==0 ) printf(“\n”); printf(“%4d”,array[i]); } void main() { int array[N]; int i; getin(array,N); for (i=0;i<N;i++) { if (i%5==0 ) printf(“\n”); printf(“%4d”,array[i]); } 程序运行结果为: 46 30 82 90 56 17 95 15 48 26 4 58 71 79 92 60 12 21 63 47 数组指针作为函数参数可以分为4种情况: ☆ 形参、实参都是数组名; ☆ 实参是数组名,形参是指针变量; ☆ 形参、实参都是指针变量; ☆ 实参是指针变量,形参是数组名。
【例9.14】 用选择排序法,将10个数升序排列。 程序思想:选择排序的基本思想是,首先在10个数中选出一个最小数并将其存入a[0];然后在剩下的9个数中再选出一个最小数并存入a[1];依次类推,共选9次。 具体操作步骤: (1) 将10个数存入数组a。 (2) 在10个数选出一个最小数存入a[0]。 (3) 在剩下的9个数中,再选出一个最小数并存入a[1] (4) 在剩下的8个数中选出一个最小数并存入a[2]。 (5) 依此类推,直到选出第9个数,剩下的一个数是最大的。 (6) 用外循环控制选第几个最小数,内循环控制从第几个数开始选。
程序如下: #include<stdio.h> void sort (int *x,int n) /*可以为:void sort(int x[],int n) */ { int i,j,k,t; for (i=0;i<n-1;i++) { k=i; /*变量k用来记录最小数的位置*/ for (j=i+1;j<n;j++) if (*(x+j)<*(x+k)) k=j; if (k!=i) {t=*(x+i);*(x+i)=*(x+k);*(x+k)=t;} } void main() int *p,i,a[10]; for (i=0;i<10;i++) scanf(“%d”,&a[i]); p=a; sort(p,10); for (p=a,i=0;i<10;i++,p++) printf(“%4d”,*p) 运行结果如下: 1 0 4 8 12 65 -76 100 -45 123↙ -76 -45 0 1 4 8 12 65 100 123
9.3.2字符指针 1.字符串的表示形式 在C语言中,可以用两种方法实现对字符串的操作。 (1)用字符数组处理字符串。 【例9.15】 void main() { char str[]=“I love china!”; printf(“%s”,str); } 或: char str[]=”I love china!”; int i; for (i=0;*(str+i)!=’\0’;i++) printf(“%c”,*(str+i)); 运行结果: I love china!
(2)用字符指针实现 将字符串的首地址赋给指针,用字符指针变量来处理字符串。 【例9.16】 用指针变量逐个输出数组中的每个字符。 void main() { char str[ ]=”I love china!”; char *p; int i; p=str; for (i=0;*(p+i)!=’\0’;i++) printf(“%c”,*(p+i)); } 或用指针变量整体输出字符数组的值。 void main() { char str[ ]=”I love china!”; char *p; int i; p=str; printf(“%s”,p); } 也可以在定义字符指针的同时初始化。 { char *str=“I love china!”; printf(“%s\n”,str);
【例9.17】 将字符串a复制到字符串b。 void main() { char a[ ]=”I love china!”; char b[20]; int i; for (i=0;*(a+i)!=’\0’;i++) *(b+i)=*(a+i); *(b+i)=’\0’; printf(“%s\n”,a); for (i=0;b[i]!=’\0’;i++) printf(“%c”,b[i]); } 用指针变量处理例9.17问题。程序如下: void main() { char a[ ]=”I love china!”,b[20],*p1,*p2; int i; p1=a;p2=b; for (;*p1!=’\0’;p1++,p2++) *p2=*p1; *p2=’\0’; printf(“%s\n”,a); for (i=0;b[i]!=’\0’;i++) printf(“%c”,b[i]); } 运行结果: I love china!
字符数组名可作函数参数,字符指针变量也可作函数参数。 2.字符指针作函数参数 字符数组名可作函数参数,字符指针变量也可作函数参数。 【例9.18】 用函数调用实现字符串的连接。 strcat12(char *p1,char *p2) { while (*p1!=’\0’) /*使p1指向第一个字符串末尾*/ p1++; while (*p2!=’\0’) /*将p2连接到第一个字符串的当前位置*/ { *p1=*p2;p1++;p2++; } *p1=’\0’; } void main() char str1[40]={“People’s Republic of ”}; char str2[ ]={“China”}; strcat12(str1,str2); printf(“%s\n”,str1); 运行结果: People’s Republic of China
字符数组和字符指针变量都能实现字符串的存储与运算,但两者之间还是有区别的,主要包括以下几点: 3.字符指针变量与字符数组 字符数组和字符指针变量都能实现字符串的存储与运算,但两者之间还是有区别的,主要包括以下几点: (1) 字符数组由若干元素组成,每个元素存放一个字符; 字符指针变量存放的是字符串的首地址。 (2) 赋值方式不同: 不能用赋值语句给字符数组赋值。 char str[14]; str=“I love china!” 这是错误的。 字符指针变量可以采用下面的形式赋值: char *p; p=“china!” 这是正确的。 但要注意赋给指针变量p的不是字符串,而是字符串的首地址。 (3)定义一个数组,在编译时分配存放n个元素的存储空间; 定义指针变量只分配存放一个地址的空间。 (4)指针变量的值是可以改变的,字符数组名则不行。
【例9.19】字符指针变化输出。 void main() { char *a=“I love China!”; a=a+7; /* a已指向了字符C*/ printf(“%s”,a); } 运行结果如下: China! /*当前地址直到字符串结束为止*/ 字符数组不能采用如下形式: char str[]={“I love China!”}; str=str+7; /*该语句是错误的*/ printf(“%s”,str); 数组名是一个常量其值是不能改变的。 (5) 可以用指针变量指向一个格式字符串,用它代替printf中的格式串。 char *format=“a=%d,b=%f\n”; printf(format ,a,b)
9.3.3 指针数组 指针数组:数组的所有元素都是存放指针的数组称为指针数组。 即:指针数组中每一个元素(下标变量)都是指针变量。 指针数组的定义形式: <存储类型> <数据类型> <*指针数组名>[数组长度][={地址列表}]; 例如: Staic int *p[4]; 由于[ ]比*的优先级高,先构成p[4]数组形式,有4 个元素:p[0],p[1],p[2],p[3]。然后再与p前面的“*”结合成为指针数组。 指针数组的用途:每个元素存放一个字符串首址,使得在处理多个字符串时非常方便。
例如:图书馆有下列一些书籍,Visual BASIC、QBASIC、Pascal、C、Visual Foxpro等 ,将这些书名进行排序。 处理方法: 先定义一个二维数组存放这些书名。 char ch[ ][14]; 存储空间如图9.9所示。 ch[0]VisualBASIC\0Ch[1]QBASIC\0Ch[2]Pascal\0Ch[3]C\0Ch[4]VisualFoxpro\0图9.9 字符数组ch[][14] 存储空间 图9.9 字符数组ch[ ][14] 存储空间
(2)定义一个指针数组 例如定义: char *p[5]; for(i=0;i<5;i++) p[i]=ch[i]; 存储空间如图9.10所示。 或: char *p[5]={“Visual BASIC”,”Qbsic”,”Pascal”,”C”,”Visual Foxpro” }; 【例9.20】 将若干字符串按字母顺序由小到大排序。 程序思想:以上面图书馆书籍名称排序为例,只需改变指针数组的指向,使其指针所指的字符串按字母从小到大排序即可。
程序如下: #include <string.h> void sort (char *xname[ ],int n) { char *temp; int i,j,k; for (i=0;i<n-1;i++) k=i; for (j=i+1;j<n;j++) if (strcmp(xname[k],xname[j])>0) k=j; if (k!=i) { temp=xname[i]; xname[i]=xname[k]; xname[k]=temp; } }
void main() { Char *name[ ]={“Visual BASIC”,”QBasic”,”Pascal”,”C”,”Visual Foxpro” }; int n=5; sort(name,n); for (i=0;i<n;i++) printf(“%s\n”,name[i]); } 运行结果: C Pascal QBasic Visual BASIC Visual Foxpro
9.4 函数与指针 9.4.1函数指针 函数名就代表函数的入口地址。 函数指针:函数的入口地址称为函数指针。即指向函数的指针变量为函数指针。 函数指针定义格式: <存储类型> <数据类型> (*指针变量名)(); 其中:“存储类型”是函数指针本身的存储类型; “数据类型”是指函数返回值的数据类型; 例如: int (*p)(); 定义一个指向整形函数的指针变量p。
【例9.21】 求a和b中的最大者。 程序如下: void main() { int max(); int (*p)(); int a,b,c; p=max; /*函数max入口地址赋给p*/ scanf(“%d,%d”,&a,&b); c=(*p)(a,b); printf(“a=%d,b=%d,max=%d”,a,b,c); } int max(int x,int y) { int z; if (x>y) z=x; else z=y; return(z);
9.4.2指针函数 指针函数:返回指针值的函数称为指针函数。 指针函数定义形式: <类型标识符> <*函数名>(参数表); 例如: int *f1(x,y); 其中f1为函数名,调用f1后得到一个指向整型数据的指针(地址)。 【例9.22】 有若干个学生成绩(每个学生有4门课),要求在用户输入学号后,能输出学生的全部成绩,用指针函数实现。
程序如下: void main() { float score[ ][4]={{66,76,86,96},{66,77,88,99},{48,78,89,90}}; float *serach(float(*pointer)[4],int n); float *p; int i,m; printf(“Enter the number of student:”); scanf(“%d”,&m); /*输入要查找的学生序号*/ printf(“The scores of No.%d are:\n”,m); p=search(score,m); for (i=0,i<4;i++) /*输出第n个学生的全部成绩*/ printf(“%6.2f”,*(p+i)); }
float *search(float (*pointer)[4],int n) /*函数的返回值为指向实型数据的指针*/ { float *pt; pt=*(pointer+n); /*把指针定位在第n个学生的第一个数据上*/ return(pt); /*函数的放回值为第n个学生成绩的首地址*/ } 运行结果如下: Enter the number of student:1 The scores of No.%d are: 66.00,77.00,88.00,99.00
9.4.3指向指针的指针 指针变量:专门用来存放另一个变量的地址的变量就称为指针变量; 指向指针的指针:用来存放指针变量的地址的变量就称为指向指针变量的指针变量。有的教材又称为二级指针变量。 定义格式: <类型标识符> **变量名; 例如:int **p;
【例9.24】 void main() { int a=12; int *p1,**p2; p1=&a; p2=&p1; printf(“%d,%d\n”,*p1,**p2); } 运行结果: 12,12
9.5 程序综合举例 假设每班人数最多不超过40人,具体人数由键盘输入,试编程打印最高分及其学号。 【例9.26】 用一维数组和指针变量作为函数参数,编程打印某班一门课成绩的最高分及其学号。 #include <stdio.h> #define ARR_SIZE 40 int FindMax(int score[], long num[], int n, long *pMaxNum); void main() { int score[ARR_SIZE], maxScore, n, i; long num[ARR_SIZE], maxNum; printf("Please enter total number:"); scanf("%d", &n); /*从键盘输入学生人数n*/ printf("Please enter the number and score:\n"); for(i=0; i<n; i++) /*输入学生的学号和成绩*/ { scanf("%ld%d", &num[i], &score[i]); } maxScore = FindMax(score, num, n, &maxNum); printf("maxScore = %d, maxNum = %ld\n", maxScore, maxNum); }
/* 函数功能:计算最高分及最高分学生的学号*/ int FindMax(int score[ ], long num[ ], int n, long *pMaxNum) { int i; int maxScore; maxScore = score[0]; /*假设score[0]为最高分*/ *pMaxNum = num[0]; for (i=1; i<n; i++) if (score[i] > maxScore) { maxScore = score[i]; /*记录最高分*/ *pMaxNum = num[i]; /*记录最高分学生的学号*/ } return (maxScore); /*返回最高分maxScore*/
【例9.27】 用二维数组和指针变量作为函数参数,编程打印3个班学生(假设每班4个学生)的某门课成绩的最高分,并指出具有该最高分成绩的学生是第几个班的第几个学生。 程序如下: #include <stdio.h> #define CLASS 3 #define STU 4 int FindMax(int score[CLASS][STU], int m, int *pRow, int *pCol); void main() { int score[CLASS][STU], i, j, maxScore, row, col; printf("Please enter score:\n"); for (i=0; i<CLASS; i++) for (j=0; j<STU; j++) scanf("%d", &score[i][j]); /*输入学生成绩*/
maxScore = FindMax(score, CLASS, &row, &col); printf("maxScore = %d, class = %d, number = %d\n",maxScore, row+1, col+1); } int FindMax(int score[ ][STU], int m, int *pRow, int *pCol) { int i, j, maxScore; maxScore = score[0][0]; /*置初值,假设第一个元素值最大*/ *pRow = 0; *pCol = 0; for (i = 0; i<m; i++) for (j = 0; j<STU; j++) if (score[i][j] > maxScore) {maxScore = score[i][j]; /*记录当前最大值*/ *pRow = i; /*记录行下标*/ *pCol = j; } /*记录列下标*/ return (maxScore); /*返回最高分*/
9.6上机实训 实训1 1.实训目的 (1)掌握指针变量的定义与引用。 (2)掌握指针与变量、指针与数组的关系。 (3)掌握用数组指针作为函数参数的方法。 (4)熟悉集成环境的调试指针程序的方法。 2.实训内容 以下均用指针方法编程: (1)下面程序有部分错误,调试下列程序,使之具有如下功能: 用指针法输入12个数,然后按每行4个数输出。写出调试过程。
void main() { int j,k,a[12],*p; for(j=0;j<12;j++) scanf("%d",p++); { printf("%d",*p++); if(j%4 == 0) printf("\n"); } (2)调试此程序时将a、p、*p设置为"watch",调试时注意指针变量指向哪个目标变量。 (3)编写一个函数void sort(int *,int count),接收主程序中数组,并按照从小到大的顺序对数组中的元素排序。 (4)自己编写一个比较两个字符串s和t大小的函数strcomp(char *s,char *t),要求s小于t时返回-1,s等于t时返回0,s大于t时返回1。在主函数中任意输入4个字符串,利用该函数求最小字符串。
3.实验要求 (1)复习指针的定义与使用方法。 (2)编写程序,运行程序并记录运行结果。 (3)将源程序、目标文件、可执行文件和实验报告存在服务器的指定文件夹。