Download presentation
Presentation is loading. Please wait.
1
第十章 指针
2
1、按变量地址存取变量值的方式称为“直接访问”方式
§10.1地址和指针的概念 为了说清楚什么是指针,必须弄清楚数据在内存中是如何存储的,又是如何读取的。 内存区的每一个字节有一个编号,这就是“地址” 。如果在程序中定义了一个变量,在对程序进行编译时,系统就会给这个变量分配内存单元。 1、按变量地址存取变量值的方式称为“直接访问”方式 printf(″%d″,i); scanf(″%d″,&i); k=i+j; 例如:
4
另一种存取变量值的方式称为“间接访问”的方式。即,将变量i的地址存放在另一个变量中。
在C语言中,指针是一种特殊的变量,它是存放地址的。假设我们定义了一个指针变量i_pointer用来存放整型变量的地址,它被分配地址为(3010)、(3011)的两个字节。可以通过语句:i_pointer =&i; 将i的地址(2000)存放到i_pointer中。这时, i_pointer的值就是(2000) ,即变量i所占用单元的起始地址。要存取变量i的值,可以采用间接方式:先找到存放“i的地址”的变量i_pointer ,从中取出i的地址(2000),然后到2000 、 2001字节取出i的值(3)。
6
指针和指针变量的定义: 一个变量的地址称为该变量的“指针”。 例如,地址2000是变量i的指针。如果有一个变量专门用来存放另一变量的地址(即指针),则它称为“指针变量”。上述的i_pointer就是一个指针变量。 指针变量的值(即指针变量中存放的值)是地址(即指针)。请区分“指针”和“指针变量”这两个概念。
7
§10.2 变量的指针和指向变量的指针变量 10.2.1 定义一个指针变量 定义指针变量的一般形式为 基类型 *指针变量名;
8
下面都是合法的定义: float *pointer_3; // pointer_3是指向float型变量的指针变量 char *pointer_4; //pointer_4是指向字符型变量的指针变量 可以用赋值语句使一个 指针变量得到另一个变 量的地址,从而使它指 向一个该变量。如: pointer_1=&i; pointer_2=&j;
9
在定义指针变量时要注意两点: 指针变量前面的“*”,表示该变量的类型为指针型变量。 例: float *pointer_1; 指针变量名是pointer_1 ,而不是* pointer_1 。 (2) 在定义指针变量时必须指定基类型。 需要特别注意的是,只有整型变量的地址才能放到指向整型变量的指针变量中。下面的赋值是错误的∶ float a; int * pointer_1; pointer_1=&a; /* 将float型变量的地址放到指向整型变量的指针变量中,错误 */
10
10.2.2 指针变量的引用 请牢记,指针变量中只能存放地址(指针), 不要将一个整数(或任何其他非地址类型的数据) 赋给一个指针变量。 例10.1 通过指针变量访问整型变量 #include <stdio.h> void main ( ) { int a,b; int*pointer_1, *pointer_2; a=100;b=10; pointer_1=&a; /*把变量a的地址赋给 pointer_1 */
11
pointer_2=&b; /*把变量b的地址赋给
printf(″%d,%d\n″,a,b); printf(″%d,%d\n″,*pointer_1, *pointer_2); }
12
对“&”和“*”运算符说明: 如果已执行了语句 pointer_1=&a; (1)&* pointer_1的含义是什么?“&”和“*”两个运算符的优先级别相同,但按自右而左方向结合,因此先进行* pointer_1的运算,它就是变量a,再执行&运算。因此,&* pointer_1与&a相同,即变量a的地址。如果有pointer_2 =&* pointer_1 ; 它的作用是将&a(a的地址)赋给pointer_2 ,如果pointer_2原来指向b,经过重新赋值后它已不再指向b了,而指向了a。
14
(2). &a的含义是什么?先进行&a运算,得a的地址,再进行. 运算。即&a所指向的变量,也就是变量a。. &a和
(2) *&a的含义是什么?先进行&a运算,得a的地址,再进行*运算。即&a所指向的变量,也就是变量a。*&a和*pointer_1的作用是一样的,它们都等价于变量a。即*&a与a等价。 (3) (*pointer_1)++相当于a++。注意括号是必要的,如果没有括号,就成为了*pointer_1++,从附录可知:++和*为同一优先级别,而结合方向为自右而左,因此它相当于*(pointer_1++)。由于++在pointer_1的右侧,是“后加”,因此先对pointer_1的原值进行*运算,得到a的值,然后使pointer_1的值改变,这样pointer_1不再指向a了。
15
例10 . 2 输入a和b两个整数,按先大后小的顺序输出
a和b。 #include <stdio.h> void main() { int *p1,*p2,*p,a,b; scanf(″%d,%d″,&a,&b); p1=&a;p2=&b; if(a<b) {p=p1;p1=p2;p2=p;} printf(″a=%d,b=%d\n\n″,a,b); printf(″max=%d,min=%d\n″,*p1,*p2); }
16
运行情况如下: 5,9↙ a=5,b=9 max=9,min=5 当输入a=5,b=9时,由于a<b,将p1和p2交换。交换前的情况见图(a),交换后见图(b)。
18
10.2.3 指针变量作为函数参数 例 对输入的两个整数按大小顺序输出 #include <stdio.h> void main() {void swap(int *p1,int *p2); int a,b; int *pointer_1,*pointer_2; scanf(″%d,%d″,&a,&b); pointer_1 =&a; pointer_2 =&b; if(a<b= swap( pointer_1 , pointer_2 ); printf(″\n%d,%d\n″,a,b); }
19
void swap(int *p1,int *p2)
{ int temp; temp=*p1; *p1=*p2; *p2=temp; }
21
例10.4 输入a、b、c 3个整数,按大小顺序输出 #include <stdio.h> void main() { void exchange(int *q1, int *q2, int *q3); int a,b,c,*p1,*p2,*p3; scanf(″%d,%d,%d″,&a, &b, &c); p1=&a;p2=&b;p3=&c; exchange (p1,p2,p3); printf(″\n%d,%d,%d\n″,a,b,c); }
22
void exchange(int *q1, int *q2, int *q3)
{ void swap(int *pt1, int *pt2); if(*q1<*q2) swap(q1,q2); if(*q1<*q3) swap(q1,q3); if(*q2<*q3= swap(q2,q3); } void swap(int *pt1, int *pt2) {int temp; temp=*pt1; *pt1=*pt2; *pt2=temp;
23
§10.3 数组与指针 一个变量有地址,一个数组包含若干元素,每个数组元素都在内存中占用存储单元,它们都有相应的地址。指针变量既然可以指向变量,当然也可以指向数组元素(把某一元素的地址放到一个指针变量中)。所谓数组元素的指针就是数组元素的地址。
24
10.3.1 指向数组元素的指针 定义一个指向数组元素的指针变量的方法,与以前介绍的指向变量的指针变量相同。例如: int a[10];
(定义a为包含10个整型数据的数组) int*p; (定义p为指向整型变量的指针变量) 应当注意,如果数组为int型,则指针变量的基类型亦应为int型。
25
对该指针变量赋值: p=&a[0]; 把a[0]元素的地址赋给指针变量p。也就是使p指向a数组的第0号元素,如图:
26
10.3.2通过指针引用数组元素 引用一个数组元素,可以用: (1) 下标法,如a[i]形式;
(1) 下标法,如a[i]形式; (2) 指针法,如*(a+i)或*(p+i)。其中a是数组名,p是指向数组元素的指针变量,其初值p=a。 例10.5 输出数组中的全部元素 假设有一个a数组,整型,有10个元素。要输出各元素的值有三种方法:
27
(1)下标法 #include <stdio.h> void main() { int a[10]; int i; for(i=0;i<10;i++) scanf(″%d″,&a[i]); printf(″\n″); printf(″%d″,a[i]); }
28
(2) 通过数组名计算数组元素地址,找出元素的值。
#include <stdio.h> void 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)); }
29
(3) 用指针变量指向数组元素。 #include <stdio.h> void 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++) printf(″%d ″,*p); }
30
例10.6 通过指针变量输出a数组的10个元素。 有人编写出以下程序: #include <stdio.h> void 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++ ) printf(″%d″,*p); }
31
这个程序乍看起来好像没有什么问题。有的人即使已被告知此程序有问题,还是找不出它有什么问题。我们先看一下运行情况:
↙ 显然输出的数值并不是a数组中各元素的值
32
解决这个问题的办法,只要在第二个for循环之前加一个赋值语句:
p=a; #include <stdio.h> void main() { int*p,i,a[10]; p=a; for(i=0;i<10;i++) scanf(″%d″,p++); printg(″\n″); p=a; for(i=0;i<10;i++,p++ ) printf(″%d″,*p); }
33
10.3.3 用数组名作函数参数 在第8章8.7节中介绍过可以用数组名作函数的参数 如: void main()
10.3.3 用数组名作函数参数 在第8章8.7节中介绍过可以用数组名作函数的参数 如: void main() {if(int arr[],int n); int array[10]; ┇ f(array,10); } void f(int arr[ ],int n) { }
34
f (int arr[ ], int n) 但在编译时是将arr按指针变量处理的,相当于将函数f的首部写成 f (int *arr, int n) 以上两种写法是等价的。 需要说明的是:C语言调用函数时虚实结合的方法都是采用“值传递”方式,当用变量名作为函数参数时传递的是变量的值,当用数组名作为函数参数时,由于数组名代表的是数组首元素地址,因此传递的值是地址,所以要求形参为指针变量。
35
例10.7 将数组a中n个整数按相反顺序存放
36
#include <stdio.h>
void main() { void inv(int x[ ],int n); 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 in verted:\n″); for(i=0;i<10;i++) printf (″%d,″,a[i]); printf (″\n″); }
37
void inv(int x[ ],int n) /*形参x是数组名*/
{ int temp,i,j,m=(n-1)/2; for(i=0;i<=m;i++) { j=n-1-i; temp=x[i]; x[i]=x[j]; x[j]=temp; } return;
38
运行情况如下: The original array: 3,7,9,11,0,6,7,5,4,2, The array has been inverted: 2,4,5,7,6,0,11,9,7,3,
40
对这个程序可以作一些改动。将函数inv中的形参x改成指针变量。
#include <stdio.h> void main() {void inv(int *x,int n); 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 in verted:\n″ ); printf (″%d,″,a[i]); printf (″\n″);}
41
void inv(int *x,int n) /*形参x为指针变量*/
{intp,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; }
42
归纳起来,如果有一个实参数组,想在函数中改变此数组中的元素的值,实参与形参的对应关系有以下4种情况:
(1) 形参和实参都用数组名,如: void main() void f(int x[ ],int n) { int a[10]; { … … f(a,10); } }
44
(2) 实参用数组名,形参用指针变量。如: void main() void f(int *x,int n) {int a[10]; { … … f(a,10); } } (3)实参形参都用指针变量。例如: void main() void f(int *x,int n) {int a[10], *p=a; { ┇ ┇ f(p,10); } }
46
(4) 实参为指针变量,形参为数组名。如: void main() void f(int x[ ],int n) {int a[10],*p=a; { ┇ ┇ f(p,10); } }
47
#include <stdio.h>
void main() { void inv(int *x,int n); int i,arr[10],*p=arr; printf(″The original array:\n ″); for(i=0;i<10;i++,p++) scanf(″%d″,p); printf(″\n″); p=arr; inv(p,10); /* 实参为指针变量 */ printf(″The array has been inverted :\n″); for(p=arr;p<arr+10;p++ ) printf(″%d″,*p); printf(″\n″); }
48
void inv(int *x,int n) {intp,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; }
49
例10.9 用选择法对10个整数按由大到小顺序排序 #include <stdio.h> void main() { void sort(int x[ ],int n); int*p,i,a[10]; p=a; for(i=0;i<10;i++) scanf(″%d″,p++); sort(p,10); for(p=a,i=0;i<10;i++) {printf(″%d″,*p);p++;} }
50
void sort(int x[ ],int n)
for(i=0;i<n-1;i++) {k=i; 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;} } }
51
10.3.4 多维数组与指针 用指针变量可以指向一维数组中的元素,也可以指向多维数组中的元素。但在概念上和使用上,多维数组的指针比一维数组的指针要复杂一些。 1. 多维数组元素的地址 先回顾一下多维数组的性质,可以认为二维数组是“数组的数组”,例 : 定义int a[3][4]={{1,3,5,7},{9,11,13,15},{17,19,21,23}}; 则二维数组a是由3个一维数组所组成的。设二维数组的首行的首地址为2000 ,则
52
表 示 形 式 含义 地 址 a 二维数组名,指向一维数组a[0],即0行首地址 2000 a[0], *(a+0), *a 0行0列元素地址 a+1,&a[1] 1行首地址 2008 a[1],*(a+1) 1行0列元素a[1][0]的地址 A[1]+2, *(a+1)+2, &a[1][2] 1行2列元素a[1][2] 的地址 2012 *(a[1]+2), *(*(a+1)+2), a[1][2] 1行2列元素a[1][2]的值 元素值为13
53
例10.10 输出二维数组有关的值 #include <stdio.h> #define FROMAT ″%d,%d\n″ void main() { int a[3][4]={1,3,5,7,9,11,13, 15,17,19,21,23}; printf(FORMAT,a,*a); printf(FORMAT,a[0] , *(a+0)); printf(FORMAT,&a[0],&a[0][0]); printf(FORMAT,a[1],a+1); printf(FORMAT,&a[1][0],*(a+1)+0); printf(FORMAT,a[2],*(a+2)); printf(FORMAT,&a[2],a+2); printf(FORMAT,a[1][0],*(*(a+ 1)+0)); }
54
某一次运行结果如下: 158,158 (0行首地址和0行0列元素地址) 158,158 (0行0列元素地址) 158,158 (0行0首地址和0行0列元素地址) 166,166 (1行0列元素地址和1行首地址) 166,166 (1行0列元素地址) 174,174 (2行0列元素地址) 174,174 (2行首地址) 9,9 (1行0列元素的值)
55
2 . 指向多维数组元素的指针变量 在了解上面的概念后,可以用指针变量指向多维数组的元素。 (1) 指向数组元素的指针变量 例10.11 用指针变量输出二维数组元素的值 运行结果如下: #include <stdio.h> void main() { int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23}; int*p; for(p=a[0];p<a[0]+12;p++) {if((p-a[0])%4==0) printf(″\n″); printf(″%4d″,*p); } }
56
可将程序最后两个语句改为 printf(″addr=%o, value=%2d\n″,p,*p); 在TC++环境下某一次运行时输出如下: addr=236,value=1 addr=240,value=3 addr=242,value=5 addr=244,value=7 addr=246,value=9 addr=250,value=11 addr=252,value=13 addr=254,value=15 addr=256,value=17 addr=260,value=19 addr=262,value=21 addr=264,value=23
57
(2) 指向由m个元素组成的一维数组的指针变量
例10.13 出二维数组任一行任一列元素的值 #include <stdio.h> void main ( ) { int a[3][4]={1,3,5,7,9,11, 13,15,17,19,21,23}; int (*p)[4],i,j; p=a; scanf(″ i=%d,j=%d″,&i,&j); printf(″a[%d,%d]=%d\n″,i, j,*(*(p+i)+j)); } 运行情况如下: i=1,j=2↙ (本行为键盘输入) a[1,2]=13 运行情况如下: i=1,j=2↙(本行为键盘输入) a[1,2]=13
58
3. 用指向数组的指针作函数参数 例 有一个班,3个学生,各学4门课,计算总平均分数以及第n个学生的成绩。这个题目是很简单的。只是为了说明用指向数组的指针作函数参数而举的例子。用函数average求总平均成绩,用函数search找出并输出第i个学生的成绩。
59
#include <sydio.h>
void main() { void average(float *p,int n); void search(float (*p)[4],int n); float score[3][4]={{65,67,70,60},{80, 87,90,81},{90,99,100,98}}; average(*score,12);/*求12个分数的平均分*/ search(score,2);/*求序号为2的学生的成绩*/ }
60
void average(float *p,int n)
p_end=p+n-1; for(;p<=p_end;p++) sum=sum+(*p); aver=sum/n; printf(″average=%5.2f\n″,aver); }
61
void search(float (*p)[4],int n)
/ * p是指向具有4个元素的一维数组的指针 */{int i; printf(″the score of No. %d are:\n″,n); for(i=0;i<4;i++) printf(″%5.2f″,*(*(p+n)+i)); } 程序运行结果如下: average=82.25 the score of No.2 are: 90.00 99.00 100.00 98.00
62
例10.14 在上题基础上,查找有一门以上课程不及格的学生,打印出他们的全部课程的成绩。
#include <stdio.h> void main() {void search(float (*p)[4],int n);/*函数声明*/ float score[3][4]={{65,57,70,60},{58,87, 90,81},{90,99,100,98}}; search(score,3); }
63
void search(float (*p)[4],int n)
for(j=0;j<n;j++) {flag=0; for(i=0;i<4;i++) if(*(*(p+j)+i)<60) flag=1; if(flag==1) { printf("No.%d fails,his scores are:\n",j+1); for(i=0;i<4;i++) printf(″%5.1f″,*(*(p+j)+i)); printf(″\n″); } } } 程序运行结果如下: No.1 fails,his scores are: 65.0 57.0 70.0 60.0 No.2 fails,his scores are∶ 58.0 87.0 90.0 81.0
64
(1) 用字符数组存放一个字符串,然后输出该字符串。
§10.4 字符串与指针 10.4.1字符串的表示形式 (1) 用字符数组存放一个字符串,然后输出该字符串。 例 10.15 定义一个字符数组,对它初始化, 然后输出该字符串 #include <stdio.h> void main() {char string[]=″I love China!″; printf(″%s\n″,string); }
65
(2) 用字符指针指向一个字符串 可以不定义字符数组,而定义一个字符指针。用字符指针指向字符串中的字符。 例10.16 定义字符指针 #include <stdio.h> void main() {charstring=″I love China!″; printf(″%s\n″,string); }
66
对字符串中字符的存取,可以用下标方法,也可以用指针方法
例10.17 将字符串a复制为字符串b #include <stdio.h> void main() {char a[ ]=″I am a boy.″,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:″); for(i=0;b[i]!=′\0′;i++) printf(″%c″,b[i]); printf(″\n″); }
67
也可以设指针变量,用它的值的改变来指向字符串中的不同的字符。
例10.18 用指针变量来处理例10.17问题。 #include <stdio.h> void main() {char a[ ] =″I am a boy. ″,b[20],*p1,*p2; int i; p1=a;p2=b; for(;*p1!=′\0′;p1++,p2++)
68
*p2=*p1; *p2=′\0′; printf(″string a is:%s\n″,a); printf(″string b is:″); for(i=0;b[i]!=′\0′;i++) printf(″%c″,b[i]); printf(″\n″); } 程序必须保证使p1和p2同步移动。
70
10.4.2 字符指针作函数参数 例10.19 用函数调用实现字符串的复制 (1) 用字符数组作参数 #include <stdio.h> void main() { void copy_string(char from[ ], char to[ ]); char a[ ]=″I am a teacher.″; char b[ ]=″you are a student.″; printf(“string a=%s\n string b=%s\n″, a,b); printf("copy string a to string b:\n "); copy_string (a,b); printf("\nstring a=%s\nstring b=%s\n",a,b); }
71
void copy_string(char from[ ], char to[ ])
{ int i=0; while(from[i]!=′\0′) {to[i]=from[i];i++;} to[i]=′\0′; } 程序运行结果如下: string a=I am a teacher. string b=you are a student. copy string a to string b: string b=I am a teacher.
72
(2) 形参用字符指针变量 #include <stdio.h> void main() { void copy_string(char *from, char *to); char *a=″I am a teacher .″; char *b=″you are a student .″; printf("string a=%s\nstring b=%s\n″,a,b); printf("copy string a to string b:\n "); copy_string(a,b); printf("\nstring a=%s\nstring b=%s\n",a,b); }
73
(3) 对 copy_string 函数还可作简化
void copy_string(char *from,char *to) { for(;*from!=′\0′;from++,to++) *to=from; *to=′\0′; } (3) 对 copy_string 函数还可作简化 1、将copy_string函数改写为 void copy_string (char *from,char *to) {while((*to=*from)!=′\0′) {to++;from++;} }
74
2、 copy_string函数的函数体还可改为
{ while((*to++=*from++)!=′\0′); } 3、copy_string函数的函数体还可写成 { while(*from!=′\0′) *to++=*from++; *to=′\0′; }
75
4、上面的while语句还可以进一步简化为下面的while语句:
它与下面语句等价: while((*to++=*from++)!=′\0′); 将*from赋给*to,如果赋值后的*to值等于′\0′,则循环终止(′\0′已赋给*to) 5、函数体中while语句也可以改用for语句: for(;(*to++=*from++)!=0;); 或 for(;*to++=*from++;);
76
6、也可用指针变量,函数copy_string可写为
void copy_string (char from[ ],char to[ ]) {char*p1,*p2; p1=from;p2=to; while((*p2++=*p1++)!=′\0′); } 10.4.3 对使用字符指针变量和字符数组的讨论 虽然用字符数组和字符指针变量都能实现字符串的存储和运算,但它们二者之间是有区别的,不应混为一谈,主要有以下几点:
77
(1) 字符数组由若干个元素组成,每个元素中放一个字符,而字符指针变量中存放的是地址(字符串第1个字符的地址),决不是将字符串放到字符指针变量中。
(2)赋值方式。对字符数组只能对各个元素赋值,不 能用以下办法对字符数组赋值。 char str[14]; str=″I love China!″; 而对字符指针变量,可以采用下面方法赋值: char*a; a=″I love China!″; 但注意赋给a的不是字符,而是字符串第一个元素 的地址。
78
(3)对字符指针变量赋初值: char *a=″I love China!″;等价于 char*a; a=″I love China!″; 而对数组的初始化: char str[14]={″I love China!″}; 不能等价于 char str[14]; str[ ]=″I love China!″;
79
(4) 如果定义了一个字符数组,在编译时为它分配内存单元,它有确定的地址。而定义一个字符指针变量时,给指针变量分配内存单元,在其中可以放一个字符变量的地址也就是说,该指针变量可以指向一个字符型数据,但如果未对它赋予一个地址值,则它并未具体指向一个确定的字符数据。 如: char str[10]; scanf(″%s″,str);是可以的。 而常有人用下面的方法,目的是想输入一个字符串,虽然一般也能运行,但这种方法是危险的 : char*a; scanf(″%s″,a);
80
应当这样: char*a,str[10]; a=str; scanf(″%s″,a); (5) 指针变量的值是可以改变的,如: 例10.20 改变指针变量的值 #include <stdio.h> void main() {char*a=″I love China!″; a=a+7; printf(″%s″,a); }
81
需要说明,若定义了一个指针变量,并使它指向一个字符串,就可以用下标形式引用指针变量所指的字符串中的字符。例10.21
#include <stdio.h> void main() {char*a=″I love China!″; int i; printf ( “ The sixth character is %c\n",a[5]); for(i=0;a[i]!=′\0′;i++) printf(″%c″,a[i]); }
82
10.5 指向函数的指针 10.5.1 用函数指针变量调用函数 可以用指针变量指向整型变量、字符串、数组,也可以指向一个函数。一个函数在编译时被分配给一个入口地址。这个函数的入口地址就称为函数的指针。 例10.22 求a和b中的大者。先列出按一般 方法的程序。
83
#include <stdio.h>
void main() { int max(int,int); int a,b,c; scanf(″%d,%d″,&a,&b); c=max(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);
84
将 main 函数改写为 #include <stdio.h> void main() { int max(int,int); int (*p)(); int a,b,c; p=max; scanf(″%d,%d″,&a,&b); c=(*p)(a,b); printf(″a=%d,b=%d,max=% d″,a,b,c); }
85
10.5.2 用指向函数的指针作函数参数 函数指针变量常用的用途之一是把指针作为参数传递到其他函数。前面介绍过,函数的参数可以是变量、指向变量的指针变量、数组名、指向数组的指针变量等。现在介绍指向函数的指针也可以作为参数,以实现函数地址的传递,这样就能够在被调用的函数中使用实参函数。它的原理可以简述如下:有一个函数(假设函数名为sub),它有两个形参(x1和x2),定义x1和x2为指向函数的指针变量。在调用函数sub时,实参为两个函数名f1和f2,给形参传递的是函数f1和f2的地址。这样在函数sub中就可以调用f1和f2函数了。
86
实参函数名 f f2 ↓ ↓ void sub(int (*x1)(int),int (*x2)(int,int)) { int a,b,i,j; a=(*x1)(i); /*调用f1函数*/ b=(*x2)(i,j);/*调用f2函数*/ … }
87
例10.23 设一个函数process,在调用它的时候,每次实现不同的功能。输入a和b两个数,第一次调用process时找出a和b中大者,第二次找出其中小者,第三次求a与b之和。
#include <stdio.h> void main() { int max(int,int); /* 函数声明 */ int min(int,int); /* 函数声明 */ int add(int,int); /* 函数声明 */ void process (int,int,int(*fun)(); /* 函数声明 */ int a,b; printf(″enter a and b:″); scanf(″%d,%d″,&a,&b);
88
printf(″max=″); process(a,b,max); printf(″min=″); process(a,b,min); printf(″sum=″); process(a,b,add); }
89
int max(int x,int y) /* 函数定义 */
{int z; if(x>y)z=x; else z=y; return(z); } int min(int x,int y) /* 函数定义 */ if(x<y)z=x; else z=y; return(z);
90
int add(int x,int y) /* 函数定义 */
{int z; z=x+y; return(z); } void process(int x,int y,int (*fun)(int,int)) {int result; result=(*fun)(x,y); printf(″%d\n″,result);
91
10.6 返回指针值的函数 一个函数可以带回一个整型值、字符值、实型值等,也可以带回指针型的数据,即地址。其概念与以前类似,只是带回的值的类型是指针类型而已。 这种带回指针值的函数,一般定义形式为 类型名 *函数名(参数表列); 例如: int *a(int x,int y);
92
例10.24 有若干个学生的成绩(每个学生有4门课程),要求在用户输入学生序号以后,能输出该学生的全部成绩。用指针函数来实现。
#include <stdio.h> void main() {float *score[ ][4]={{60,70,80,90}, {56,89,67,88},{34,78,90,66}}; float*search(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);
93
p=search(score,m); for(i=0;i<4;i++= printf(″%5.2f\t″,*(p+i)); } float * search(float (*pointer)[4],int n) { float *pt; pt=*(pointer+n); return(pt); } 运行情况如下: enter the number of student:1↙ The scores of No. 1 are:
94
例10.25 对上例中的学生,找出其中有不及格课程的学生及其学生号。
#include <stdio.h> void main() {float score[ ][4]={{60,70,80,90},{56, 89,67,88},{34,78,90,66}}; float search(float (*pointer)[4]); float*p; int i,j;
95
for(i=0;i<3;i++) {p=search(score+i); if(p==*(score+i)) {printf(″No.%d scores:″,i); for(j=0;j<4;j++) printf(″%5.2f″,*(p+j)); printf(″\n″);} } }
96
10.7指针数组和指向指针的指针 10.7.1 指针数组的概念 一个数组,若其元素均为指针类型数据,称为指针数组,也就是说,指针数组中的每一个元素都相当于一个指针变量。一维指针数组的定义形式为 类型名数组名[数组长度]; 例如: int*p[4];
98
例10.26 将若干字符串按字母顺序(由小到大)输出。
#include <stdio.h> #include <string.h> void main() {void sort(char *name[ ],int n); void printf(char *name[ ],int n); char *name[ ]={"Follow me","BASIC","Great Wall″,"FORTRAN","Computer design"}; int n=5; sort(name,n); print(name,n); }
99
void sort(char *name[ ],int n)
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) temp=name[i]; name[i]=name[k]; name[k]=temp;} } }
100
void print(char *name[ ],int n)
{int i; for(i=0;i<n;i++) printf(″%s\n″,name[i]); } 运行结果为: BASIC Computer design FORTRAN Follow me Great Wall
101
10.7.2 指向指针的指针 怎样定义一个指向指针数据的指针变量呢?如下: char**p; p的前面有两个*号。*运算符的结合性是从右到左,因此**p相当于*(*p),显然*p是指针变量的定义形式。如果没有最前面的*,那就是定义了一个指向字符数据的指针变量。现在它前面又有一个*号,表示指针变量p是指向一个字符指针变量的。*p就是p所指向的另一个指针变量。
102
例10.27 使用指向指针的指针 #include <stdio.h> void main() {char *name[]={"Follow me","BASIC","Great Wall″,"FORTRAN","Computer design"}; char **p; int i; for(i=0;i<5;i++) {p=name+i; printf(″%s\n″,*p); }
103
例10.28 一个指针数组的元素指向整型数据的简单例子
#include <stdio.h> void main() {int a[5]={1,3,5,7,9}; int *num[5]={&a[0],&a[1], &a[2],&a[3],&a[4]}; int **p,i; p=num; for(i=0;i<5;i++= { printf(″%d ″,**p); p++; }
104
10.7.3 指针数组作main函数的形参 指针数组的一个重要应用是作为main函数的形参。在以往的程序中,main函数的第一行一般写成以下形式:void main() 括弧中是空的。实际上,main函数可以有参数,例如:void main(int argc,char *argv[ ]) argc和argv就是main函数的形参。main函数是由操作系统调用的。那么,main函数的形参的值从何处得到呢?显然不可能在程序中得到。实际上实参是和命令一起给出的。也就是在一个命令行中包括命令名和需要传给main函数的参数。命令行的一般形式为命令名 参数1 参数2……参数n
105
如果有一个名为file1的文件,它包含以下的main函数:
void main(int argc,char *argv[ ]) {while(argc>1) {++argv; printf(″%s\n″,argv); --argc; } 在DOS命令状态下输入的命令行为 file1 China Beijing 则执行以上命令行将会输出以下信息: China Beijing
106
10.8有关指针的数据类型和指针运算的小结 10.8.1有关指针的数据类型的小结 定义 含义 int i; 定义整型变量i int*p;
p为指向整型数据的指针变量 int a[n]; 定义整型数组a,它有n个元素 int *p[n]; 定义指针数组p,它由n个指向整型数据的指针元素组成 int (*p)[n]; p为指向含n个元素的一维数组的指针变量 int f(); f为带回整型函数值的函数 int *p(); p为带回一个指针的函数,该指针指向整型数据 int (*p)(); p为指向函数的指针,该函数返回一个整型值 int **p; p是一个指针变量,它指向一个指向整型数据的指针变量
107
指针运算小结 (1) 指针变量加(减)一个整数 例如:p++、p--、p+i、p-i、p+=i、p-=i等。 (2) 指针变量赋值 将一个变量地址赋给一个指针变量。如: p=&a; (将变量a的地址赋给p) p=array; (将数组array首元素地址赋给p) p=&array[i];(将数组array第i个元素 的地址赋给p) p=max;(max为已定义的函数,将max的入口 地址赋给p) p1=p2;(p1和p2都是指针变量,将p2的 值赋给p1)
108
(3) 指针变量可以有空值,即该指针变量不指向任何变量,可以这样表示:p=NULL;
(4) 两个指针变量可以相减 如果两个指针变量都指向同一个数组中的元素,则两个指针变量值之差是两个指针之间的元素个数
109
(5) 两个指针变量比较 若两个指针指向同一个数组的元素,则可以进行比较。指向前面的元素的指针变量“小于”指向后面元素的指针变量。 void指针类型 ANSIC新标准增加了一种“void”指针类型,即可定义一个指针变量,但不指定它是指向哪一种类型数据的。ANSIC标准规定用动态存储分配函数时返回void指针,它可以用来指向一个抽象的类型的数据,在将它的值赋给另一指针变量时要进行强制类型转换使之适合于被赋值的变量的类型。例如: char*p1; void*p2; … p1=(char *)p2;
110
同样可以用(void *)p1将p1的值转换成void *类型。如:
p2=(void *)p1; 也可以将一个函数定义为void *类型,如: void *fun(char ch1,char ch2) 表示函数fun返回的是一个地址,它指向“空类型”,如需要引用此地址,也需要根据情况对之进行类型转换,如对该函数调用得到的地址要进行以下转换: p1=(char *)fun(ch1,ch2);
Similar presentations