420 likes | 629 Views
C 语言程序设计. 主讲:沈济南. TEL: 13971887071 E-mail: shenjinan@163.com. 第 15 讲. 主讲内容: 第七章 指针运指针与函数、数组 7.3 指针运算 7.4 指针与函数. 7.3 指针运算. 7.3.1 指针的赋值运算 指针的赋值运算分以下几种类型: 1 )任何类型指针均可赋 NULL ,表示指针不指向任何目标。 例如: char *p; p=NULL; int *p1; p1=NULL; double *p2; p2=NULL;.
E N D
C语言程序设计 主讲:沈济南 TEL:13971887071 E-mail:shenjinan@163.com
第15讲 • 主讲内容: • 第七章 指针运指针与函数、数组 7.3 指针运算 7.4 指针与函数
7.3指针运算 • 7.3.1指针的赋值运算 指针的赋值运算分以下几种类型: • 1)任何类型指针均可赋 NULL,表示指针不指向任何目标。 例如: char *p; p=NULL; int *p1; p1=NULL; double *p2; p2=NULL;
(2)相同类型指针可相互赋值,不同类型指针不能相互赋值。例如:(2)相同类型指针可相互赋值,不同类型指针不能相互赋值。例如: char ch='A',*pc=&ch; int n=10,*pn=&n; int m=100, *pm=&m; double d=10.0, *pd=&d; pd=pc; pc=pd; pd=pn; /* 错误 */ pn=pm; /* 正确 */ • (3)不能对指针赋一个字面常量地址,也不能通过读入赋值给指针。例如: int a[4]={1,2,3,4}; int *p; p=2000; printf(”%x”,p); /* 错误 */ p=a; /* 正确 p 指向数组 a 的首地址 */
图7.5a 变量定义后 (a) 图7.5b 执行P=&m后 (a) • (4)指针赋值是改变指针的指向,比较抽象,须加深感性认识。形象描述指针赋值最好的方法是图示法。例如: • int n=10, m=20, *p=&n, *p1=&n; • 变量定义后,指针p和p1都指向变量n,如图7.5(a)所示。执行p=&m ;语句后,指针p指向m, 不再指向n,如图7.5(b)所示。 图7.5a 变量定义后 图7.5b 执行P=&m后
7.3.2指针的算术运算 • 1. 指针加整数和指针减整数 图7.6指针加减整数 int a[4]={1,2,3,4}; int *p=a,*p1=p+3; 指针加或减整数运算(p+n,p-n),其结果是指针值,即地址。如图7.6所示,指针加或减 n,其结果是第n个数据的首地址,指针变量的值不变。p+0和p1-3表达式值为ffd0,p+1和p1-2表达式值为ffd2,p+2和p1-1表达式值为ffd4,p+3和p1-0表达式值为ffd6。
图7.6 指针加减整数 但p和p1的值未改变,还是ffd0和ffd6。注意,指针变量p 、p1是指向数组a的数组元素的指针,不是指向数组a的指针。 图7.6 指针加减整数
7.3.2指针的算术运算 • 2. 指针自增和自减运算 • int a[4]={1,2,3,4}; • int *p=a,*p1=p+3; • 指针加1或减 1,其结果是指针值,即地址(如图7.7)。指针加1或减 1的结果是指针变量指向后一个数据或前一个数据的首地址,即加一个数据的字节数,或减一个数据的字节数。
图7.7 指针自增自减 • 指针变量的值改变。执行++p;语句,p的值由ffd0改为ffd2。执行--p1;语句,p1的值由ffd6改为ffd4。 图7.7 指针自增自减
图7.8指针相减运算 7.3.2指针的算术运算 • 3. 指针相减运算 int a[4]={1,2,3,4},n;图7.8指针相减运算 int *p=a,*p1=p+3; 指针相减运算,运算结果是两指针相隔的 数据数目。如图7.8所示,例如: p-p1 运算结果是 –3 p1-p 运算结果是 3 图7.8指针相减运算
图7.9 指针关系运算 7.3.3指针的关系运算 • int a[4]={1,2,3,4},n; • int *p=a,*p1=p+3; • 指针关系运算的实质是比较地址的高低,而关系运算的结果为 0 或 1。如图7.9所示。图7.9 指针关系运算 • 例如: • p>=p1 运算结果是 0 • p1>=p 运算结果是 1 • p1==p 运算结果是 0 • p1!=p 运算结果是 1 • p+2==p1-1 运算结果是 1 图7.9 指针关系运算
7.3.4指针的下标运算 • 指针的下标运算一般和数组联系起来一起使用。 int a[4]={1,2,3,4},n; int *p=a; • 上面第一行定义了一个一维的整型数组a,第二行把数组a的首地址赋给了指针变量p,那么指针变量p就指向数组a。p的值就是数组a的首地址ffd0,在值上也等于数组第一个元素的地址&a[0],如图7.10所示。我们在引用数组元素的时候可以通过数组名的下标运算,也可以用指针的下标运算。
图7.10 指针的下标运算 • 例如:图7.10 指针的下标运算 • p[0] 等价于a[0]; • p[1] 等价于a[1]; • p[2] 等价于a[2]; • p[3] 等价于a[3]; • 那么我们能不能就说指针变量p等价于数组a呢?不能。因为p是地址变量,而a是数组名,是地址常量。实际上,我们在前面就已经说过,下标运算符左边的操作数是地址类型的数据。比如指针变量p或数组名a,其值都是地址。地址和下标运算符进行运算,就可以访问内存中不同的空间。 图7.10 指针的下标运算
7.4指针与函数 • 7.4.1指针作为函数的参数 • 指针可作为函数的形参和函数的返回值类型。指针作为函数形参,可达到形参共享实参的目的。 • 如果修改指针所指向的目标,能使函数带回多个值。而修改指针本身,不能使函数带回多个值。当形参为指针变量时,其对应实参可以是指针变量或存储单元地址。下面以交换两个整数为例进行说明。
【例 7.3】 • 【例 7.3】 形参和实参都为普通变量,不能实现两个变量值的交换。 /*源程序名:CH0703.C*/ /*01*/ #include <stdio.h> /*02*/ main() /*03*/ { /*04*/ int x,y; /*05*/void swap1(int x,int y); /*06*/ scanf("%d%d",&x,&y);
/*07*/ swap1(x,y); /*08*/ printf("x=%d y=%d\n",x,y); /*09*/ } /*10*/ void swap1(int x,int y) /* 交换两个整数 -- 交换形参值 */ /*11*/ { /*12*/ int temp; /*13*/ temp=x; x=y; /*14*/ y=temp;图7.11传值 /*15*/ printf("x=%d y=%d\n",x,y); /*16*/ }
图7.11传值 • 上例中的参数传递方式是值传递的方式。传值是单向传递,即将实参的值传递给对应的形参,而不可能将形参的值反传递给实参。传递结束,实参和形参不存在任何联系,因此,形参值的修改不会影响实参值。图7.11形象显示出:形参x、y的值进行了交换。但是,由于形参x、y的值不能修改实参x、y的值,所以实参x、y的值保持原值。 图7.11传值
【例 7.4】 • 【例 7.4】将例7.3中的 形参和实参都改为指针变量,不能实现两个变量值的交换。 /*源程序名:CH0704.C*/ /*01*/ #include <stdio.h> /*02*/ main()图7.12 传址,修改指针 /*03*/ { /*04*/ int x,y; /*05*/ int *px,*py;/*实参*/ /*06*/ void swap2(int *px,int *py); /*07*/ scanf("%d%d",&x,&y);
/*08*/ px=&x; /*09*/ py=&y; /*10*/ swap2(px,py); /*11*/ printf("x=%d y=%d\n",x,y); /*12*/ } /*13*/ void swap2(int *px,int *py) /* 交换两个整数 -- 交换形参值(地址) */ /*14*/ { /*15*/ int *temp; /*16*/ temp=px; px=py; /*17*/ py=temp; /*18*/ printf("x=%d y=%d\n",*px,*py); /*19*/ }
图7.12 传址,修改指针 • 传址(传地址值)是将实参的地址传递给对应的形参,实现了形参共享实参,即形参px、py指针所指向的目标就是实参x、y存储单元。图7.12显示出了形参共享实参概念。同时也显示出:形参px、py的指针值进行了交换,即x指针改为指向实参y,而y指针改为指向实参x。但是,实参x、y值没有被修改,仍保持原值。 图7.12 传址,修改指针
【例 7.5】 • 【例 7.5】地址作实参,实现两个变量值的交换 • /*源程序名:CH0705.C*/ • /*01*/ #include <stdio.h> • /*02*/ void main() • /*03*/ { • /*04*/ int x,y; • /*05*/ void swap3(int *px,int *py); • /*06*/ scanf("%d%d",&x,&y); • /*07*/ swap3(&x, &y);
/*08*/ printf("x=%d y=%d\n",x,y); /*09*/ } /*10*/ void swap3(int *px,int *py) /*交换两个整数-- 交换实参值 */ /*11*/ { /*12*/ int temp; /*13*/ temp=*px; *px=*py; *py=temp; /*14*/ printf("x=%d y=%d\n",*px,*py); /*15*/ }
图7.13 传址,修改目标 • 传址(传地址值),实现了形参共享实参。在swap3函数中的下列语句: • temp=*px; *px=*py; *py=temp; • 是交换x、y指针所指向的目标,即交换实参x、y的值,且向调用函数传回两个值,如图7.13所示。 图7.13 传址,修改目标
【例 7.6】 • 【例 7.6】指针变量作实参,实现两个变量值的交换。 /*源程序名:CH0706.C*/ /*01*/ #include <stdio.h> /*02*/ void main() /*03*/ { /*04*/ int x,y; /*05*/ int *px,*py; /*06*/ void swap4(int *px,int *py); /*07*/ scanf("%d%d",&x,&y); /*08*/ px=&x;
/*09*/ py=&y; /*10*/ swap4(px, py); /*11*/ printf("x=%d y=%d\n",x,y); /*12*/ } /*13*/ void swap4(int *px,int *py) /* 交换两个整数 -- 交换实参值 */ /*14*/ { /*15*/ int temp; /*16*/ temp=*px; *px=*py; *py=temp; /*17*/ printf ("x=%d y=%d\n",*px,*py); /*18*/ }
函数在调用过程中传递的是指针变量的值,也就是变量x,y的地址,和例7.5一样是传址的过程,实现了形参共享实参。因此可以改变两个变量的值。函数在调用过程中传递的是指针变量的值,也就是变量x,y的地址,和例7.5一样是传址的过程,实现了形参共享实参。因此可以改变两个变量的值。 • 从swap4(传址,修改目标)函数看出:通过指针类型形参,且修改它所指向的目标,可以使函数带回多个值。
7.4.2 返回指针的函数 • 前面提到指针可以作为函数的参数,其实指针也可作为函数的返回值类型。在处理动态数据结构时,经常遇到返回指针的函数。 • 返回指针类型函数的一般格式: • 类型名 * 函数名(形参表); (有参函数) • 类型名 * 函数名(); (无参函数)
例如: int *fun(int a,int b) { 函数体语句 } 函数fun即是一个指针函数,要求返回一个int型的指针,这就要求在return语句中,有返回指针变量或地址的语句,形如: return(&变量名); 或return(指针变量); • 特别要注意的是,返回局部变量的地址是不允许的。因为函数返回,局部变量自动撤消。动态变量的地址、全局变量的地址和静态变量的地址都可作为函数的返回值。下面看一个简单的例题。
【例 7.7】 • 【例 7.7】指针函数示例 /*源程序名:CH0707.C*/ /*01*/ #include <stdio.h> /*02*/ void main() /*03*/ { /*04*/ int a,b,*p; /*05*/ int *min(int x,int y); /*06*/ int *minp(int *,int *); /*07*/ scanf("%d,%d",&a,&b); /*08*/ p=min(a,b);
/*09*/ printf("\nmin=%d",*p); • /*10*/ p=minp(&a,&b); • /*11*/ printf("\nminp=%d",*p); • /*12*/ } • /*13*/ int *min(int x,int y) • /*14*/ { • /*15*/ if(x<y) return(&x); /* 变量x的地址作为指针函数的返回值 */ • /*16*/ else return(&y); • /*17*/ } • /*18*/ int *minp(int *x,int *y) • /*19*/ { • /*20*/ int *q; • /*21*/ q=*x<*y?x:y; • /*22*/ return(q); /* 指针变量q作为指针函数的返回值 */ • /*23*/ }
7.4.3指向函数的指针 • 指向函数的指针称为函数指针。函数指针是函数的入口地址,函数名的值。可用函数指针调用函数。函数指针也可作为函数的形参。 • 函数指针的一般定义格式: 类型名 (* 指针变量名)();(无参数表) 类型名 (* 指针变量名)(类型名表);(有参数表)
例如: • int (*p)(); • int (*p)(int x,int y); • 上面的示例中,表示p指向一个返回整型值的函数。注意int (*p)() 与int *p();的差别,前者定义的p为一个指针变量(是指向函数的),后者定义的p 是一个函数,其返回值为指针,格式不能写错。
刚定义的指向函数的指针变量,也像其它指针变量一样,要赋于地址才能引用。当将某个函数的入口地址赋给指向函数的指针变量时,就可用该指针变量来调用所指向的函数。函数名代表了函数的入口地址,我们只须将要通过指针调用的函数名赋给指针即可。如max(x,y)是一个求x,y中最大值的整型函数,p为指向函数的整型指针,则下面的赋值语句:刚定义的指向函数的指针变量,也像其它指针变量一样,要赋于地址才能引用。当将某个函数的入口地址赋给指向函数的指针变量时,就可用该指针变量来调用所指向的函数。函数名代表了函数的入口地址,我们只须将要通过指针调用的函数名赋给指针即可。如max(x,y)是一个求x,y中最大值的整型函数,p为指向函数的整型指针,则下面的赋值语句: • p=max; • 表示p指向函数max(),即是将max函数的入口地址值赋给指针变量p。因为函数名代表函数的入口地址。无须考虑函数参数。
注意,下面的语句是错误的: • p=max(m,n); • 因为max(m,n)是函数调用,其返回值是整型值,不是指针,故不能赋给指针变量p。 • 若t是一个整型变量,则实现函数调用的两种方法如下: • (1)直接调用 t=max(m,n); • (2)间接调用 t=(*p)(m,n);
两者是等价的。要注意的是,用函数指针调用函数是间接调用,没有参数类型说明,C编译系统也无法进行类型检查,因此,在使用这种形式调用函数时要特别小心。实参一定要和指针所指函数的形参类型一致。两者是等价的。要注意的是,用函数指针调用函数是间接调用,没有参数类型说明,C编译系统也无法进行类型检查,因此,在使用这种形式调用函数时要特别小心。实参一定要和指针所指函数的形参类型一致。 • 用函数指针调用函数的一般形式是: • (* 指针变量名)(实参表)
【例 7.8】 • 【例 7.8】应用函数指针调用函数 -- 求 a 和 b 的最大值。 /*源程序名:CH0708.C*/ /*01*/ #include <stdio.h> /*02*/ int max(int a,int b); /*03*/ void main() /*04*/ { /*05*/ int a,b,Max; /*06*/ int (* pfun)(int,int);/* 函数指针定义 */ /*07*/pfun=max;/* 将函数名赋给函数指针 */
/*08*/ printf("输入两整数:"); /*09*/ scanf("%d %d",&a,&b); /*10*/ Max=(* pfun)(a,b); /* 函数调用 */ /*11*/ printf("%d和%d 的较大值是 %d\n",a,b,Max); /*12*/ } /*13*/ int max(int a,int b) /*14*/ { /*15*/ return (a>b?a:b); /*16*/ }
运行结果: 输入两整数:10 20 10和20 的最大值是 20 • 函数指针也可以作为函数的参数,此时,当函数指针每次指向不同的函数时,执行不同的函数来完成不同的功能,这也是函数指针作为函数参数的意义所在。
【例 7.9】 • 【例 7.9】函数指针作为函数的形参 /*源程序名:CH0709.C*/ /*01*/ #include <stdio.h> /*02*/ #include <stdlib.h> /*03*/ int callfun(int(* funp)(int a,int b),int a,int b); /*04*/ int fmax(int a,int b); /*05*/ int fmin(int a,int b); /*06*/ void main() /*07*/ { /*08*/ int a,b; /*09*/ printf("输入两整数:");
/*10*/ scanf("%d %d",&a,&b); /*11*/ printf("%d和%d 的最大值是 %d\n",a,b,callfun(fmax,a,b)); /*12*/ printf("%d和%d 的最小值是 %d\n",a,b,callfun(fmin,a,b)); /*13*/ } /*14*/ int callfun(int(*funp)(int a,int b),int a,int b) /*15*/ { /*16*/ int fvalue; /*17*/ fvalue=(*funp)(a,b); return fvalue; /*18*/ } /*19*/ int fmax(int a,int b) /*20*/ { /*21*/ return (a>b?a:b); /*22*/ } /*23*/ int fmin(int a,int b) /*24*/ { /*25*/ return (a>b?b:a); /*26*/ }
运行结果: 输入两整数:10 20 10和20 的最大值是 20 10和20 的最小值是 10
C语言程序设计 主讲:沈济南 TEL:13971887071 E-mail:shenjinan@163.com