C Primer Plus学习 五十 函数和指针

来源:互联网 发布:淘宝如何上传照片 编辑:程序博客网 时间:2024/05/06 05:12
       正像关于声明的讨论中指出的那样,声明指向函数的指针是可以的。您可能想知道这样讨厌的家伙有些什 么用处。典型的用法是,一个函数指针可以作为另一个函数的参数,告诉第二个函数使用哪一个函数。例如, 对一个数组进行排序涉及到比较两个元素以决定哪个元素放在前面。如果元素是数字,可以使用 > 运算符。更普 遍的是,元素可能是一个字符串或一个结构,需要一个函数调用来执行比较。C库里的qsort ()函数是对任何 类型的数组都适用的,只要告诉它用哪个函数来比较元素。为此,它接受一个指向函数的指针来作为一个参数。 然后,无论数组元素的类型是整数、字符串或是结构,qsort ()都使用这个函数对元素进行排序。
        我们更进一步介绍函数指针。首先,函数指针是什么意思?假定一个指针指向一个int变量,它保存 着这个int变量在内存中存储的地址。同样,函数也有地址,这是因为函数的机器语言实现是由载入到内 存的代码组成。指向函数的指针中保存着函数代码起始处的地址。
       其次,当声明一个数据指针时,必须声明它指向的数据的类型。当声明一个函数指针时,必须声明它 指向的函数类型。要指定函数类型,就要指出函数的返回类型以及函数的参量类型。例如,考虑以下原型:
void ToUpper (char *): //把字符串转换为大写
       函数ToUpper ()的类型是“具有char*类型的参量,返回类型是void的函数”。要声明指向这种类 型的函数的指针pf,可以这样做:
void (*pf) (char *); // pf是一个指向函数的指针
       从这个声明中可以看出,第一对圆括号将运算符*和Pf结合在一起,这意味着Pf是一个指向函数的指 针。这就使得(*pf)是一个函数,并使(char*)作为该函数的参量列表,void作为其返回类型。可能创 建这个声明最简单的方法是注意到它用表达式(*pf)来代替函数名ToUpper。因此,如果想要声明一个指 向某一特定类型函数的指针,可以声明一个这种特定类型的函数,然后用一个(* pf)形式的表达式来替 代函数名,以创建一个函数指针声明。就像先前提到过的那样,由于有运算符优先级的规则,所以第一个圆括号是必需的。省略掉圆括号会导致完全不同的情况:

void *pf (char *); // pf是返回一个指针的函数

有了函数指针之后,可以把适当类型的函数的地址陚给它。在这种场合中,函数名可以用来表示函数 的地址:

void ToUpper (char *);

 void ToLower (char *);

 int round (double);

 void (*pf) (char *);

 pf = ToUpper;//合法,ToUpper是函数ToUpper ()的地址 

 pf = ToLower: //合法,ToLower是函数ToLower O的地址

pf = round;  //无效,round是错误类型的函数

pf = ToLower (): //无效,ToLower ()不是地址

        最后一种赋值方式也是不正确的,因为不能在一个陚值的语句中使用一个void类型的函数。注意,指 针pf可以指向任何接受一个char*参数并且返回类型为void的函数,而不能指向具有其他特性的函数。
        正像可以使用一个数据指针来访问数据一样,也可以使用函数指针来访问函数。奇怪的是,有两个逻 辑上不一致的语法规则来实现这样的操作,请看下面的举例说明:

void ToUpper (char *):

 void ToLower (char *);

 void (*pf) (char *);

 char mis[] = "Nina Metier":

 pf = ToUpper:

(*pf) (mis): // 把ToUpper作用于mis (语法 1)

pf = ToLower:

pf (mis); // 把 ToLower 作用于 mis (语法 2)

        每种方法听起来都是有道理的。第一种方法:因为pf指向ToUpper函数,*pf就是ToUppei函数,因 此表达式(*pf) (mis)与ToUpper (mis) —样。从ToUpper和pf的声明中就能看出ToUpper和(* pf)是等价的。第二种方法:因为函数名是一个指针,可以互换地使用指针和函数名,因此pf (mis)与ToLower (mis)—样。从pf的賦值语句中就能看出pf和ToLower是等价的。历史上,贝尔实验室的C和UNIX的 开发者采用第一种观点,而Berkeley的UNIX的扩展者采用第二种观点。K&RC不允许第二种形式。但是 为了保持与现有代码的兼容性,ANSI C把这二者作为等价形式全部接受。
正如数据指针最常见的用法之一是作为函数的参数一样,函数指针最普遍的用法之一也是作为函数的 参数。例如,考虑以下函数原型:

void show (void (* fp) (char *), char * str);

        这看起来很杂乱,但它声明了两个参量pf和str。参量fp是一个函数指针,str是一个数据指针。更具 体一点,fp指向接受一个char*参量且返回类型为void的函数,str指向一个char值。因此,给定前面的 声明,可以使用像下面这样的函数调用:

show (ToLower, mis): /* show ()使用 ToLower ()函数:fp=ToLower */

show (pf,mis): /* show ()使用由 pf 指向的函数:fp=pf */

show ()如何使用传递过来的函数指针呢?它使用语法尔()或()来调用函数:

void show (void (* fp) (char *),char * str)

{ 1

(*fp) (str): /*把所选函数作用于str */

puts (str); /* S不结果 */

}

       例如,这里show ()首先把fp指向的函数作用于字符串str来转换str,然后显示转换后的字符串。 顺便提一句,带有返回值的函数能以两种不同的方式作为其他函数的参数。例如,考虑下面的情况:

functionl (sqrt): /* 传递 sqrt 函数的地址 */

function2 (sqrt (4.0)); /* 传递 sqrt 函数的返回值 */

        第一个语句传递了函数sqrt ()的地址,functionl ()可能会在代码中使用该函数。第二个语句先调 用函数sqrt (),求出它的值,然后将返回值(在本例中是2.0)传递给fiinction2 ()。

// func_ptr.c --使用函数指针\

#include<stdio.h>

#include<string.h>

#include<ctype.h>

#define MAX 81



char showmenu(void);

void eatline(void);        // 读至行未

void show(void(*fp)(char *),char *str);

void ToUpper(char *);     //把字符串转换为大写

void ToLower(char *);     //把字符串转换为小写

void Transpose(char *);   //大小写转置 

void Dummy(char *);       //不改变字符串

int main(void)

{

char line[MAX];

char copy[MAX];

char choice;

void (*pfun)(char *);//指向一个函数,该函数接受

// 一个char *参数,并且没有返M值

puts("Enter a string (enpty line to quit):");

while(gets(line)!=NULL&&line[0]!='\0')

{

while((choice=showmenu())!='n')

{

switch(choice)// switch语句用来设置指针

{

case 'u':pfun=ToUpper;break;

case 'l':pfun=ToLower;break;

case 't':pfun=Transpose;break;

case 'o':pfun=Dummy;break;

}

strcpy(copy,line);//// 为 show ()制作一份拷贝

show(pfun,copy);////使用用户选择的函数

}

puts("Enter a string (empty line to quit):");

}

puts("Bye!");

return 0;

}

char showmenu(void)

{

char ans;

puts("Enter menu choice:");

puts("u)uppercase l)lowercase");

puts("t)transposed case o)origianl case");

puts("n)next string");

ans=getchar();//获取用户的响应

ans=tolower(ans);// 转换为小写

eatline();// 剔除行中剩余部分

while(strchr("ulton",ans)==NULL)

{

puts("Please enter a u,l,t,o,or n:");

ans=tolower(getchar());

eatline();

}

return ans;

}

// 剔除行中剩余部分

void eatline(void)

{

while(getchar()!='\n')

continue;

}

void ToUpper(char *str)

{

while(*str)

{

*str=toupper(*str);

str++;

}

}

void ToLower(char *str)

{

while(*str)

{

   *str=tolower(*str);

str++;

}

}

void Transpose(char *str)

{

while(*str)

{

if(islower(*str))

*str=toupper(*str);

else if(isupper(*str))

*str=tolower(*str);

str++;

}

}

void Dummy(char *str)

{

// leaves string unchanged

}

void show(void (* fp)(char *), char * str)

{

    (*fp)(str); // apply chosen function to str

    puts(str);  // display result

}

下面是一个运行示例:
Enter a string (empty line to quit): Does C make you feel loopy?
Enter menu choice: u) uppercase 1) lowercase
t) transposed case o) original case n) next string
t
dOES C MAKE YOU FEEL LOOPY?
Enter menu choice:
u) uppercase 1) lowercase
t) transposed case o) original case n) next string
1
does c make you feel loopy?
Enter menu choice:
u) uppercase 1) lowercase
t) transposed case o) original case n) next string n
Enter a string (empty line to quit):
Bye!

       注意,函数ToUpper ()、ToLower ()、Transpose ()和Dummy ()都是相同类型的,因此4个函数 都可以赋值给指针pfun。这个程序用pfim作为show ()的参数,但是也可以直接将4个函数名称中的任 何一个作为参数,就像show (Transpose,copy) —样。
       在这种情况下您可以使用typedef。例如,示例程序还可以这样做:

typedef void (*V_FP_CHARP) (char *):

 void show (V_FP_CHARP fpt char *):

V_FP_CHARP pfun;




0 0
原创粉丝点击