读《C Primer Plus》有感2——指向函数指针再举例及答惑

来源:互联网 发布:开淘宝店铺的流程 编辑:程序博客网 时间:2024/05/20 06:09

        在上一篇(《指向函数指针举例》)中提到使用函数指针有两种逻辑上不一致的语法规则来实现这样的操作,并以问题的形式提出讨论,这几天看《C Primer Plus》找到了合理的解释。请看下面的例子:

void ToUpper(char *);void ToLower(char *);void (*pf)(char *);char mis[] = "Nina Metier";pf = ToUpper;(*pf)(mis);pf = ToLower;pf(mis);
         每种方法都似乎有道理。第一种方法:因为pf指向ToUpper函数,*pf就是ToUpper函数,因此表达式(*pf)(mis)与ToUpper(mis)是一样的。从ToUpper和pf的声明中就能看出ToUpper和(*pf)是等级的。第二种方法:因为函数名是一个指针,可以互换地使用指针和函数名,因此pf(mis)和ToLower(mis)一样。从pf的赋值语句就能看出pf与ToLower是等价的。为什么呢?说出来很狗血,是因为C的历史原因。

       历史上,贝尔实验室的C和UNIX的开发者采用第一种观点,而Berkeley的UNIX的扩展者采用了第二种观点。K&R C不允许第二种形式。但是为了保持与现代代码的兼容性,ANSI C把二者座位等价形式全部接受。笔者装了这么久的B完全是因为今天上课前还不知道这个原因,而且这个问题一直困扰着我,石头终落地,但略感失望。

       为了不这么狗血就结束了,这里再举一个使用函数指针的例子。

#include <stdio.h>#include <string.h>#include <ctype.h>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(){char line[81];char copy[81];char choice;void (*pfun)(char *);         //指向一个函数,该函数接受一个char *参数,并且没有返回值puts("Enter a string(empty 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)Original Case");puts("n)Next String");ans = getchar();            //获取用户的响应ans = tolower(ans);         //转换大小写EatLine();                  //剔除行中剩余部分while(strchr("ulton", ans) == NULL){puts("Please enter a u, l, t, o, 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){}void Show(void (*fp)(char *), char *str){(*fp)(str);                //把用户选择的函数作用于str puts(str);                 //输出str} 

       再次提起注意,函数Toupper(),ToLower(),Transpose(),Dummy()都是相同类型的,因此4个函数都可以赋值给指针pfun。这个程序用pfun作为show()的参数,但是也可以直接将4个函数名称中的任一个作为参数,就像Show(Transpose, copy)一样。

       至于菜单处理,函数ShowMenu()给出了几种技术。首先,代码:

ans = getchar();            //获取用户的响应ans = tolower(ans);         //转换大小写
和:
ans = tolower(getchar());
给出两种方法。这两种方法都可以将用户的输入转换为一种大小写形式,这样就不用检查'u',又检查'U'等等。

       函数EatLine()剔除输入行的剩余部分,这在两个方面很有用。第一,要输入一个选择,用户会键入一个字母,然后再按下回车键,这将产生一个换行符。如果不事先去掉这个换行符,它将作为下一个用户响应被读入。第二,假设用户键入整个单词uppercase而不是u作为响应,如果没有EatLine()函数,程序会把单词uppercase的每个字符当作一个单独的响应。有了EatLine(),程序只会处理u,并丢弃掉输入行的剩余部分。

       其次,ShowMenu()函数是设计用来只将正确的选择返回给主函数。为了完成该任务,程序使用了头文件string.h中的标准库函数strchr():

while(strchr("ulton", ans) == NULL)
        这个函数在字符串"ulton"中找出字符ans首次出现的位置,并返回一个指向该位置的指针。如果没有找到这个字符,函数返回空指针。因此上面这个while循环判断条件和一下判断条件的作用相同,但使用起来更方便:

while(ans != 'n' && ans != 'l' && ans != 't' && ans != 'o' && ans != 'n')

        需要检查的选择越多,使用strchr()就会越方便。

      希望通过此篇及上一篇讲解中的两个稍大的例子能够到达廓清函数指针中一些概念的目的。这里特此感谢《C Primer Plus》及该书的作者Stephen Prata。