C基础篇之函数指针的介绍与运用(内含有简易四则运算计算器)

最近在探究Objective-C中block的实现原理,然后就不自觉的复习了一下C语言的函数指针。正所谓万变不离其宗,虽说OC中的block跟简单的函数指针相比已经大有不同,不过二者的表现形式还是有很多相似的地方。

首先做一个声明:本文中的一些基础理论知识,来自其他的技术博客或者论坛,为了尊重原创,在这里将尽可能完整无损的呈现给想要夯实一下基础知识的小伙伴。

在开始之前,可以先下载作者为这篇文章所写的demo:简易四则运算器,总共代码在100行左右,通过这个小demo来一窥函数指针的大概。

四则运算计算器.gif

(附赠一款录制gif的工具LICEcap,使用上跟苹果的QuickTime相像,需要划定一个录制范围,当最后结束录制的时候会自动为你生成gif图。)

函数指针是什么?

先来看函数调用是怎么回事。一个函数占用一段连续内存。当调用一个函数时,实际上是跳转到函数入口地址,执行函数体的代码,完成后返回。如何找到对应的入口地址?这是由函数名来标记的,实际上,函数名就是函数的入口地址。

函数指针是一种特殊类型的指针,它指向一个函数的入口地址。

注意:除了void类型指针是无类型的指针外,其他所有指针都是有对应类型的,例如int *pintstruct studentdata *psdata等,只有指明了指针所指的数据类型,编译器才能为指针分配或预计分配相应大小的存储空间,指针的算术运算如pint++等才是有意义的。因此,定义了某种类型的指针之后,除非使用强制类型转换,那么它只能指向相应数据类型的变量或常量,不同类型的指针或数据之间不可混用。所以指针的类型实际上是一种身份标志的作用

函数指针如何表明自己的身份呢?为了避免混乱,必须也要作出相应规定,不同函数的函数指针不能混用。例如,int func1(int arg11, char arg12)int func2(char arg)的函数指针就不能混用,要定义可以指向func1的函数指针应该这样:

 int (*pfunc1)(int, char) = func1;

定义可以指向func2的函数指针则该如下:

int (*pfunc2)(char) = func2;

从函数指针的定义可以看出,函数指针的类型实际上是由函数签名决定的。函数签名就象是函数的身份证,一个函数的函数签名是独一无二的,具有相同函数签名的函数实际上就是同一函数。函数签名包括函数名、函数形参类型的有序列表和函数返回值类型。

一个函数指针的定义规定了它只能指向特定类型的函数。如果两个函数的形参列表和返回值类型相同,只有函数名和函数体不同,则可以使用相同类型的函数指针。

例如,如果还有一个函数int func3(char arg),则上面定义的可以指向函数func2的函数指针也可以用于指向func3,即:
  pfunc2 = func3;
再使用pfunc2(char ARG)就可以调用函数func3,这时指令计数器(PC)指向函数入口,从此开始执行函数体代码。

如何使用函数指针?

  • 定义合适类型的函数指针变量:int (*pfunc)(int, int);
  • 给函数指针变量赋值,使它指向某个函数入口:int example(int, int); pfunc = example;/将函数入口地址赋给函数指针变量/
  • 使用函数指针来调用相应的函数;
    retval = pfunc(10, 16); 或者:retval = (*pfunc)(10, 16);

上面两句都与retval = example(10, 16);等价。

理解:一个指针变量p实际上也和普通的变量一样,要占存储空间(通常与平台的虚拟地址一样宽),也有其自身的存储地址&p;不同的是,在指针变量p的值有特殊的意义,它是另外一个变量或常量的地址值,也就是说,在地址为&p的存储单元上存放着另外一个数据的地址。因此,p实际上是将p看作它指向的数据的地址来使用,操作符是引用相应地址中的数据,也就是对地址为p的存储单元中存放的数据进行操作。

为什么要使用函数指针?

前面介绍了函数指针的基本知识和使用规范。下面介绍函数指针的实际用途。不过首先要对前面的知识再做一个补充,因为下面的应用很可能用到这一特性。前面指出,除函数名之外的函数签名内容(函数返回值类型和形参列表)决定了函数指针的类型。实际上还有一种特殊的或说通用的函数指针,在定义这类函数指针时,只需要指定函数返回值类型,而留空形参列表,这样就可以指向返回值类型相同的所有函数。例如:
int (*pfunc)();
这样定义的pfunc就可以指向前面提到的func1func2,因为他们都返回整型值。
注意:int (*pfunc)()int (*pfunc)(void)不是一回事,后者不允许接受任何参数。

函数指针最常见的三个用途是:

  • 作为参数传递给其他函数。这样可以把多个函数用一个函数体封装起来,得到一个具有多个函数功能的新函数,根据传递的函数指针变量值的不同,执行不同的函数功能。这是函数嵌套调用难以实现的。参数的传递可以由程序员设定,也可以由用户输入读取,因此具有较大的灵活性和交互性。另外还可以用于回调函数。使用void配合,还可以将对不同数据类型的数据进行相同处理的多个函数封装为一个函数,增强函数的生命力。

  • 用于散转程序。这种程序首先建立一个函数表(实际上是一个函数指针数组),表中存放了各个函数的入口地址(或函数名),根据条件的设定来查表选择执行相应的函数。这样也可以将多个函数封装为一个函数或者程序,散转分支条件可以由程序员设定,也可以由用户输入读取,甚至是外设的某种特定状态(这种状态可以是不受人为控制的)。

  • 实现C的面向对象的类的封装。C语言中的struct与C++中的class有很大不同,除了缺省的成员属性外(struct的成员缺省为public的,可随意使用,而class成员缺省为private的),struct还很难实现类成员函数的封装。struct的成员一般都是数据成员,而非函数成员。因此,为了在C语言中,为某个struct定义一套自己的函数对结构数据成员进行操作,可以在struct结构体中增加函数指针变量成员,在初始化时使它指向特定函数即可。

基础的理论知识就介绍这些,下面来举例分析四则运算计算器demo中对函数指针的运用。

首先是定义加减乘除的基本运算如下。这是最基础的运算,不需要考虑调用的顺序。

long long add(int a,int b){
    
    return a + b;
}

long long int sub(int a,int b){
    
    return a - b;
}

long long int mul(int a ,int b){
    
    return a*b;
    
}
long long int divi(int a,int b){
    
    return a/b;
}

然后观察上面的函数,发现除了函数名不一样外,返回值与参数类型都是一样的,所以可以用一个函数指针来指向它们。

函数指针的声明如下所示:

typedef long long int (*FUNC)();

FUNC pfunc;

首先定义了一个函数指针的类型:FUNC,这样我们就可以更加方便的使用这个类型来声明函数指针变量了。下面的FUNC pfunc;就是声明了一个名为pfunc的函数指针变量。

最后就是求和运算了

double calculator(long long x,long long y,FUNC func){
    
    double result;

    result = (*func)(x,y);
    
    return result;
    
}

其中的func函数指针会根据我们所点击的运算符的不同而指向不同的函数,这样就实现了一个非常简单的计算器了。

最后附上demo地址

参考资料:
http://www.360doc.com/content/13/1104/12/13670635_326518097.shtml

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容