1 / 33

第五章 指针和函数

第五章 指针和函数. 5.1 指针的概念、指针变量的定义和引用 5.2 指针变量作函数的参数 5.3 函数的指针与函数调用 5.4 指向函数的指针变量作函数参数 5.5 返回指针值的函数. 5.1 指针的概念、指针变量的定义和引用. 变量和地址 指针变量的概念 指针变量的定义 指针变量的初始化 指针变量的引用. …………. 2000. i. 5. 2001. 2002. j. 3. 2003. ‘H’. 2004. ch. 2005. 2006. f. 3.14. 2007. 2008. 2009. ………….

lucia
Download Presentation

第五章 指针和函数

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. 第五章 指针和函数 • 5.1指针的概念、指针变量的定义和引用 • 5.2 指针变量作函数的参数 • 5.3 函数的指针与函数调用 • 5.4指向函数的指针变量作函数参数 • 5.5返回指针值的函数

  2. 5.1指针的概念、指针变量的定义和引用 • 变量和地址 • 指针变量的概念 • 指针变量的定义 • 指针变量的初始化 • 指针变量的引用

  3. ………… 2000 i 5 2001 2002 j 3 2003 ‘H’ 2004 ch 2005 2006 f 3.14 2007 2008 2009 ………… 变量和地址 • 地址:一个变量名代表内存中的一个存储单元,每个存储单元都有一个编号,这就是“地址”。 int i,j; char ch; float f; i=5; j=3; ch=’H’; f=3.14; 房间—变量名 房客—变量值 房间号—地址 编译或函数调用时为变量分配内存单元 内存中每个字节有一个编号——地址 变量是对程序中数据 存储空间的抽象

  4. …... 整型变量i 2000 20 2001 2002 2003 2004 变量ptr 2005 变量地址(指针) 指针变量 2006 地址存入 指针变量 指向 …... 变量值 变量 指针变量的概念 • 指针:一个变量的地址。 • 指针变量:专门存放另一变量地址的变量。 指针 变量的内容 变量的地址 2000 指针变量

  5. 指针变量的定义 [存储类型] 数据类型 *指针变量名; 表示定义指针变量 不是‘*’运算符 合法标识符 int i,j,*pi, *pj; float f; float *pf; 指针变量本身的存储类型 指针的目标变量的数据类型 注: (1) int *p1, *p2;指针变量名是p1,p2 ,不是*p1,*p2。 (2) 指针变量只能指向定义时所规定类型的变量。 (3) 指针变量定义后,变量值不确定,应用前必须先赋值。

  6. 指针变量的初始化 一般形式: [存储类型] 数据类型 *指针名=初始地址值; 例 int i; int *p=&i; 例main( ) { int x; static int *p=&x; .............. } () 赋给指针变量, 不是赋给目标变量 变量必须已说明过 类型应一致 例int *p=&i; int i; () 例 int i; int *p=&i; int *q=p; 用已初始化指针变量作初值 不能用auto变量的地址 去初始化static型指针

  7. 指针变量的引用 • &:取变量的地址。单目运算符、优先级为2、右结合性。 设有程序段代码: int i,*ptr; i=20; ptr=&i; ptr和i的关系如图

  8. 指针变量的引用 • *:取指针变量所指地址中的内容,与&为互逆运算。单目运算符、优先级为 2、右结合性。 • 有程序段: int x=20,y,*ptr; ptr=&x; y=*ptr; 该段程序的意思是:定义整型变量x(初值为20)和y以及指向整型的指针变量ptr。将变量x的地址赋给指针变量ptr,然后以指针变量ptr的值为内存单元地址,将该单元的数据取出赋给变量y。即相当于执行语句y=x;

  9. 注意: 乘法运算符号和指针操作符号*形式上相同,位运算符号与(&)与指针运算符号&也相同。但是这些运算符号之间没有任何关系,编译器根据他们在程序中所处的位置来解释他们,在使用的时候要注意它们使用的场合。

  10. 例5-1 • #include "stdio.h" • main() • { • int num_int=12, *p_int;/*定义一个指向int型数据的指针变量p_int */ • float num_f=3.14, *p_f; /*定义一个指向float型数据的指针变量p_f */ • char num_ch='p', *p_ch; /*定义一个指向char型数据的指针变量ph */ • p_int=&num_int; /*取变量num_int的地址,赋值给p_int */ • p_f=&num_f; /*取变量num_f的地址,赋值给p_f */ • p_ch=&num_ch; /*取变量num_ch的地址,赋值给p_ch */ • printf("num_int=%d, *p_int=%d", num_int, *p_int); • printf("num_f=%4.2f, *p_f=%4.2f\n", num_f, *p_f); • printf("num_ch=%c, *p_ch=%c\n", num_ch, *p_ch); • } [程序演示]

  11. 程序运行结果: num_int=12, *p_int=12 num_f=3.14, *p_f=3.14 num_ch=p, *p_ch=p 程序说明: (1)头三行的变量定义语句──指针变量的定义 注意:此时的指针变量p_int、p_f、p_ch,并未指向某个具体的变量(称指针是悬空的)。使用悬空指针很容易破坏系统,导致系统瘫痪。

  12. (2)中间三行的赋值语句──取地址运算(&) 取地址运算的格式: &变量 例如,&num_int、&num_f、&num_ch的结果,分别为变量num_int、num_f、num_ch的地址。 注意:指针变量只能存放指针(地址),且只能是相同类型变量的地址。 例如,指针变量p_int、p_f、p_ch,只能分别接收int型、float型、char型变量的地址,否则出错。 (3)后三行的输出语句──指针运算(*) 使用直接访问和间接访问两种方式,分别输出变量num_int、num_f、num_ch的值。 注意:这三行出现在指针变量前的星号“*”是指针运算符,访问指针变量所指向的变量的值,而非指针运算符。

  13. 指针的算术运算: • 若p=&i; 有 pn  &i sizeof(i)n • p++, p--, p+n, p-n, p+=n, p-=n的含义 例5-2 • #include <stdio.h> • void main() • { int a=1,b=2,c=3,*ptr; • ptr=&b; • printf("b:Address %x,Value %d\n",ptr,b); • ptr++; • printf("c:Address %x,Value %d\n",ptr,c); • ptr-=2; • printf("a:Address %x,Value %d\n",ptr,a); • }

  14. 在程序的一次执行中,输出结果可能是(不同的计算机变量存储的位置可能不同):在程序的一次执行中,输出结果可能是(不同的计算机变量存储的位置可能不同): • b:Address fff2,Value 2 • c:Address fff4,Value 3 • a:Address fff0,Value 1 • 从程序的执行结果中可以看出,程序中定义的三个整型变量a、b、c在内存中依次存放,首先我们让指针变量ptr指向变量b,输出变量b的存放地址为fff2;之后将指针变量作增一运算,指针变量的值实际增加1*2=2,此时ptr指向变量c,输出其地址值为fff4;最后对指针变量作ptr-2操作,让ptr指向变量a,输出其地址值fff0。

  15. 指针变量的逻辑运算 : • 例5-3: • #include <stdio.h> • void main() • { int a=1,b=2,*aptr,*bptr; • aptr=&a; • bptr=&b; • printf("a:Address %x;b:Address %x\n",aptr,bptr); • printf("aptr>bptr=%d\n",aptr>bptr); • printf("aptr<=bptr=%d\n",aptr<=bptr); • printf("aptr&&bptr=%d\n",aptr&&bptr); • } • 在程序的一次执行中,输出结果可能是: • a:Address fff4,b:Address fff2 • aptr>bptr=1 • aptr<=bptr=0 • aptr&&bptr=1

  16. 对于指针变量,下列运算是没有意义的或者是不允许的:对于指针变量,下列运算是没有意义的或者是不允许的: (1)除加减某一整数运算以外的其他算术运算,如:乘、除、求模等; (2)两个指针变量的加法运算是没有意义的,无论它们指向的是同一种数据类型还是不同的数据类型;例如有两个指针变量ptra和ptrb,那么ptra+ptrb没有任何意义。 (3)空指针变量是无意义的而且是危险的。定义一个指针后没有使其指向一个确定的地址,这个指针变量称为空指针,其错误信息是:Null point assigment。当使用空指针时,其结果是不可预料的,严重时可能造成系统瘫痪。

  17. 例5-4输入x、y,z三个整数,输出最大值和最小值(使用指针方式实现)。 • 分析:找出三个数的最大值和最小值可以先找两个数中的最大值和最小值,然后再和第三个数比较就可以找出三个数的最大和最小值。 • 算法描述: • 算法开始 • 定义数据对象: • x,y,z为整型变量 • pmax,pmin为整型指针变量。 输入x,y,z if(x>y)then • 指针变量赋值:pmax=x; 指针变量赋值:pmin=y; • Else • 指针变量赋值:pmax=y; 指针变量赋值:pmin=x; • if( z>pmax) then • pmax=z; • if(z<pmin) then • pmin=z; • 输出结果pmax,pmin。 • 算法结束

  18. 根据算法描述编写程序代码如下: • #include <stdio.h> • void main() • { int x,y,z,*pmax,*pmin;printf("Input three numbers:\n"); • scanf("%d%d%d",&x,&y,&z); • if(x>y) • { pmax=&x; • pmin=&y; • } • else • { pmax=&y; • pmin=&x; • } • if(z>*pmax) • pmax=&z; • if(z<*pmin) • pmin=&z; • printf("max=%d\nmin=%d\n",*pmax,*pmin); • }

  19. 5.2 指针变量作函数的参数 1.指针变量,既可以作为函数的形参,也可以作函数的实参。 2.指针变量作实参时,与普通变量一样,也是“值传递”,即将指针变量的值(一个地址)传递给被调用函数的形参(必须是一个指针变量)。 注意:被调用函数不能改变实参指针变量的值,但可以改变实参指针变量所指向的变量的值。

  20. 例5-4输入两个整数,将数从大到小排列 • #include <stdio.h> • void main() • { void swap(int *ptr1,int *ptr2); • int a,b,*p1,*p2; • printf("Input a,b:"); • scanf("%d%d",&a,&b); • printf("a=%d,\tb=%d\n",a,b); • p1=&a; • p2=&b; • if(a<b) • swap(p1,p2); • printf(“after swap:\n”); • printf("a=%d,\tb=%d\n",a,b); • }

  21. void swap(int *x,int *y) • { int temp; • temp=*x; • *x=*y; • *y=temp; • } • input a,b:7 10(用户从键盘输入数据) • a=7,b=10 • after swap: • a=10,b=7

  22. 例子中调用函数swap(p1,p2),在函数swap中对*ptr1和*ptr2操作其实就是访问main函数中变量a和b。通过函数体中的局部变量temp,使变量a和b的值被修改。调用时,给予了a和b的地址作为参数,函数swap运行后间接修改了a和b的值。变量在内存中的存储结构如图所示。

  23. 5.3函数指针与函数调用 1.函数指针的概念 一个函数在编译时,被分配了一个入口地址,这个地址就称为该函数的指针。 可以用一个指针变量指向一个函数,然后通过该指针变量调用此函数。 2.指向函数的指针变量 (1)定义格式 函数类型 (*指针变量)( );注意:“*指针变量”外的括号不能缺,否则成了返回指针值的函数。 例如,int (*fp)(); /* fp为指向int函数的指针变量*/(2)赋值 函数名代表该函数的入口地址。因此,可用函数名给指向函数的指针变量赋值。指向函数的指针变量=[&]函数名;注意:函数名后不能带括号和参数;函数名前的“&”符号是可选的。 (3)调用格式 (*函数指针变量)([实参表])

  24. 例如有如下定义: int (*function)(); 定义了一个指向返回值为整型的无参函数的指针(简称为指向整型函数的指针)function。 • (3)用指向函数的指针变量来调用函数 定义指向函数的指针后,就可以将一个函数的名字赋给该指针变量。在给函数指针变量赋值时,只需要给出函数名而不必给出参数。例如: 函数说明为:void swap(int *ptr1,int *ptr2); 指向函数的指针变量定义为:void (*fptr)(int *ptr1,int *ptr2); 则将函数swap的地址赋给指针变量fptr的表达式为:fptr=swap; 对于有了确定的指向的指向函数的指针变量施以指针运算(星号运算)时,就是使程序控制转移到指针指向的地址去执行该函数的函数体。

  25. 例5-5 从键盘输入一个大于1的正整数n,当n为偶数时,计算 • 1+1/221/42۰۰++1/n2 • ,当n为奇数时,计算 • 1+1/32+1/52۰۰۰++1/n2 • 要求使用指向函数的指针变量来实现该程序。 • 算法描述: • 算法开始 • 定义ptr /* ptr是指向函数的指针变量*/ • 输入n • If(n>1) then • If(n是偶数) then • ptr<- 函数fun1的起始地址 • else • ptr<- 函数fun2的起始地址 • 输出计算结果 • Else • 系统提示输入错误 • 算法结束

  26. 根据以上算法描述写出程序代码如下: • #include <stdio.h> • double func1(int x),func2(int y); • void main() • { double (*fptr)(int); /*定义指向函数的指针变量fptr*/ • int n; • printf(“input a number:”); • scanf(“%d”,&n); • if(n>1) • { if(n%2==0) • fptr=func1;/*n为偶数,指针变量ptr指向函数func1()*/ • else • fptr=func2; /*n为奇数,指针变量ptr指向函数func2()*/ • printf(“value=%9.4f\n”,(*fptr)(n)); • } • else • printf(“error!\n”); • }

  27. double func1(int x) • { int k; • double value; • value=1.0; • for(k=2;k<=x;k=k+2) • value=value+(1/(double)k)*(1/(double)k); • return value; • } • double func2(int y) • { int k; • double value; • value=1.0; • for(k=3;k<=y;k=k+2) • value=value+(1/(double)k)* (1/(double)k); • return value; • } • [程序演示]

  28. 使用指向函数的指针可以很方便的调用函数,但是在使用的过程中要注意以下事项:使用指向函数的指针可以很方便的调用函数,但是在使用的过程中要注意以下事项: • (1)在给函数指针变量赋值时,只需要给出函数名,不必要给出参数。如例子中“fptr=func1;”。 • (2)调用前必须给函数指针变量赋值。如例子中运行语句(*p)(n)前,要对函数指针变量进行赋值语句操作:“fptr=func1;”或者“fptr=func2;”。 • (3)函数指针fptr可以先后指向同类型的不同的函数,如:fptr=func1; fptr=func2; • (4)前面学过对于指针变量可以进行加减运算,但是对于函数指针来说它的运算是没有意义的。比如说fptr为函数指针变量,则fptr=fptr+1或fptr=fptr-1是没有意义的。

  29. 5.4指向函数的指针变量作函数参数 指向函数的指针变量的常用用途之一,就是将函数指针作参数,传递到其它函数。 函数名作实参时,因为要缺省括号和参数,造成编译器无法判断它是一个变量还是一个函数,所以必须加以说明。函数说明的格式,与第7章中介绍的一样。 注意:对指向函数的指针变量,诸如p+i、p++/p--等运算是没有意义的。

  30. 例5-6 求函数f1(x)=1+x2在区间[0,1]和函数f2(x)=1/(1+16x2)在区间[0,2.0]的定积分。 • 算法描述: • (1)对两个被积函数,各定义一个函数,类型为double,形参x,类型为double. • (2)定义定积分通用函数:double collect(double (*p)(float x),float a,float b,int n)。它有四个形参,p为函数指针名,类型为double与被积函数类型相同。形参a为积分下限,b为积分上限,n为区间等份数。 • (3)主函数中分别用被积分的函数名字做实参数,两次调用定积分通用函数collect。积分上下限和等份数n直接做实参。

  31. 根据算法描述写出程序代码如下: • #include <stdio.h> • void main() • { int n; • double y1,y2; • double fun1(float x); • double fun2(float x); • double collect(double (*p)(float x),float a,float b,int n); • y1=collect(fun1,0.0,1.0,200); • y2=collect(fun2,0.0,2.0,200); • printf("y1=%f\ny2=%f\n",y1,y2); • } • double collect(double (*p)(float x),float a,float b,int n) • { int i; • float f,h,area; • h=(b-a)/n; • area=((*p)(a)+(*p)(b))/2.0; • for(i=1;i<n;i++) • area+=(*p)(a+i*h); • return(area*h); • }

  32. double fun1(float x) /*定义1+x2的C函数形式*/ • { float f; • f=1+x*x; • return(f); • } • double fun2(float x) /*定义1/(1+16x2)的C函数形式*/ • { float f; • f=1/(1+16*x*x); • return(f); • } [程序演示]

  33. 5.5返回指针值的函数 • 任何一个函数都要返回一个值,这个返回值的数据类型可以是整型、实型(、字符型等基本数据类型,也可以是空类型(void)。除此之外,C语言中还有一种函数在调用后可以返回一个指针型数据(地址),这种函数称为返回指针值的函数。返回指针值的函数的定义形式如下: • 存储类型 数据类型 *函数名(形式参数表); • 例如有函数定义的头部为:float *funcname(float x,float y) • 说明:(1)float表示函数返回的指针用于指向实型对象。 • (2)funcname是函数名,函数名前面的星号表示函数funcname是一个返回指针值的函数,其返回的值是指向实型数据的指针值。 • 可以将float *funcname(float x,float y)与float *ptr对照来理解。定义ptr时不加*是普通的实型变量,加了*后ptr就是实型指针变量,存储的是实型变量的地址。同样,funcname前面不加*是普通的函数,返回值是实型,加了*后就是返回指针值的函数。 • (3)实型变量x和y是函数的形式参数。 • (4)主调函数体中要对返回指针值的函数作声明。

More Related