1 / 42

C 语言程序设计

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;.

nova
Télécharger la présentation

C 语言程序设计

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. C语言程序设计 主讲:沈济南 TEL:13971887071 E-mail:shenjinan@163.com

  2. 第15讲 • 主讲内容: • 第七章 指针运指针与函数、数组 7.3 指针运算 7.4 指针与函数

  3. 7.3指针运算 • 7.3.1指针的赋值运算 指针的赋值运算分以下几种类型: • 1)任何类型指针均可赋 NULL,表示指针不指向任何目标。 例如: char *p; p=NULL; int *p1; p1=NULL; double *p2; p2=NULL;

  4. (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 的首地址 */

  5. 图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后

  6. 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. 图7.6 指针加减整数 但p和p1的值未改变,还是ffd0和ffd6。注意,指针变量p 、p1是指向数组a的数组元素的指针,不是指向数组a的指针。 图7.6 指针加减整数

  8. 7.3.2指针的算术运算 • 2. 指针自增和自减运算 • int a[4]={1,2,3,4}; • int *p=a,*p1=p+3; • 指针加1或减 1,其结果是指针值,即地址(如图7.7)。指针加1或减 1的结果是指针变量指向后一个数据或前一个数据的首地址,即加一个数据的字节数,或减一个数据的字节数。

  9. 图7.7 指针自增自减 • 指针变量的值改变。执行++p;语句,p的值由ffd0改为ffd2。执行--p1;语句,p1的值由ffd6改为ffd4。 图7.7 指针自增自减

  10. 图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指针相减运算

  11. 图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 指针关系运算

  12. 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所示。我们在引用数组元素的时候可以通过数组名的下标运算,也可以用指针的下标运算。

  13. 图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 指针的下标运算

  14. 7.4指针与函数 • 7.4.1指针作为函数的参数 • 指针可作为函数的形参和函数的返回值类型。指针作为函数形参,可达到形参共享实参的目的。 • 如果修改指针所指向的目标,能使函数带回多个值。而修改指针本身,不能使函数带回多个值。当形参为指针变量时,其对应实参可以是指针变量或存储单元地址。下面以交换两个整数为例进行说明。

  15. 【例 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);

  16. /*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*/ }

  17. 图7.11传值 • 上例中的参数传递方式是值传递的方式。传值是单向传递,即将实参的值传递给对应的形参,而不可能将形参的值反传递给实参。传递结束,实参和形参不存在任何联系,因此,形参值的修改不会影响实参值。图7.11形象显示出:形参x、y的值进行了交换。但是,由于形参x、y的值不能修改实参x、y的值,所以实参x、y的值保持原值。 图7.11传值

  18. 【例 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);

  19. /*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*/ }

  20. 图7.12 传址,修改指针 • 传址(传地址值)是将实参的地址传递给对应的形参,实现了形参共享实参,即形参px、py指针所指向的目标就是实参x、y存储单元。图7.12显示出了形参共享实参概念。同时也显示出:形参px、py的指针值进行了交换,即x指针改为指向实参y,而y指针改为指向实参x。但是,实参x、y值没有被修改,仍保持原值。 图7.12 传址,修改指针

  21. 【例 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);

  22. /*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*/ }

  23. 图7.13 传址,修改目标 • 传址(传地址值),实现了形参共享实参。在swap3函数中的下列语句: • temp=*px; *px=*py; *py=temp; • 是交换x、y指针所指向的目标,即交换实参x、y的值,且向调用函数传回两个值,如图7.13所示。 图7.13 传址,修改目标

  24. 【例 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;

  25. /*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*/ }

  26. 函数在调用过程中传递的是指针变量的值,也就是变量x,y的地址,和例7.5一样是传址的过程,实现了形参共享实参。因此可以改变两个变量的值。函数在调用过程中传递的是指针变量的值,也就是变量x,y的地址,和例7.5一样是传址的过程,实现了形参共享实参。因此可以改变两个变量的值。 • 从swap4(传址,修改目标)函数看出:通过指针类型形参,且修改它所指向的目标,可以使函数带回多个值。

  27. 7.4.2 返回指针的函数 • 前面提到指针可以作为函数的参数,其实指针也可作为函数的返回值类型。在处理动态数据结构时,经常遇到返回指针的函数。 • 返回指针类型函数的一般格式: • 类型名 * 函数名(形参表); (有参函数) • 类型名 * 函数名(); (无参函数)

  28. 例如: int *fun(int a,int b) { 函数体语句 } 函数fun即是一个指针函数,要求返回一个int型的指针,这就要求在return语句中,有返回指针变量或地址的语句,形如: return(&变量名); 或return(指针变量); • 特别要注意的是,返回局部变量的地址是不允许的。因为函数返回,局部变量自动撤消。动态变量的地址、全局变量的地址和静态变量的地址都可作为函数的返回值。下面看一个简单的例题。

  29. 【例 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);

  30. /*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*/ }

  31. 7.4.3指向函数的指针 • 指向函数的指针称为函数指针。函数指针是函数的入口地址,函数名的值。可用函数指针调用函数。函数指针也可作为函数的形参。 • 函数指针的一般定义格式: 类型名 (* 指针变量名)();(无参数表) 类型名 (* 指针变量名)(类型名表);(有参数表)

  32. 例如: • int (*p)(); • int (*p)(int x,int y); • 上面的示例中,表示p指向一个返回整型值的函数。注意int (*p)() 与int *p();的差别,前者定义的p为一个指针变量(是指向函数的),后者定义的p 是一个函数,其返回值为指针,格式不能写错。

  33. 刚定义的指向函数的指针变量,也像其它指针变量一样,要赋于地址才能引用。当将某个函数的入口地址赋给指向函数的指针变量时,就可用该指针变量来调用所指向的函数。函数名代表了函数的入口地址,我们只须将要通过指针调用的函数名赋给指针即可。如max(x,y)是一个求x,y中最大值的整型函数,p为指向函数的整型指针,则下面的赋值语句:刚定义的指向函数的指针变量,也像其它指针变量一样,要赋于地址才能引用。当将某个函数的入口地址赋给指向函数的指针变量时,就可用该指针变量来调用所指向的函数。函数名代表了函数的入口地址,我们只须将要通过指针调用的函数名赋给指针即可。如max(x,y)是一个求x,y中最大值的整型函数,p为指向函数的整型指针,则下面的赋值语句: • p=max; • 表示p指向函数max(),即是将max函数的入口地址值赋给指针变量p。因为函数名代表函数的入口地址。无须考虑函数参数。

  34. 注意,下面的语句是错误的: • p=max(m,n); • 因为max(m,n)是函数调用,其返回值是整型值,不是指针,故不能赋给指针变量p。 • 若t是一个整型变量,则实现函数调用的两种方法如下: • (1)直接调用 t=max(m,n); • (2)间接调用 t=(*p)(m,n);

  35. 两者是等价的。要注意的是,用函数指针调用函数是间接调用,没有参数类型说明,C编译系统也无法进行类型检查,因此,在使用这种形式调用函数时要特别小心。实参一定要和指针所指函数的形参类型一致。两者是等价的。要注意的是,用函数指针调用函数是间接调用,没有参数类型说明,C编译系统也无法进行类型检查,因此,在使用这种形式调用函数时要特别小心。实参一定要和指针所指函数的形参类型一致。 • 用函数指针调用函数的一般形式是: • (* 指针变量名)(实参表)

  36. 【例 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;/* 将函数名赋给函数指针 */

  37. /*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*/ }

  38. 运行结果: 输入两整数:10 20 10和20 的最大值是 20 • 函数指针也可以作为函数的参数,此时,当函数指针每次指向不同的函数时,执行不同的函数来完成不同的功能,这也是函数指针作为函数参数的意义所在。

  39. 【例 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("输入两整数:");

  40. /*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*/ }

  41. 运行结果: 输入两整数:10 20 10和20 的最大值是 20 10和20 的最小值是 10

  42. C语言程序设计 主讲:沈济南 TEL:13971887071 E-mail:shenjinan@163.com

More Related