打印
[开发工具]

从来没想到用函数指针?很难想象你会是C语言高手

[复制链接]
192|12
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
duo点|  楼主 | 2023-9-11 10:21 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
记得刚开始工作时,一位高手告诉我,说,longjmp和setjmp玩得不熟,就不要自称为C语言高手。当时我半信半疑,为了让自己向高手方向迈进,还是花了一点时间去学习longjmp和setjmp的用法。
    后来明白那不单是跳来跳去那样简单,而是一种高级的异常处理机制,在某些情况下确实很有用。
    为了显示自己的技巧,也在自己的程序中用过几次。渐渐发现这样的技巧带来的好处是有代价的,破坏了程序的结构化设计,程序变得很难读,尤其对新手来说。
    终于明白这种技巧不过是一种调味料,在少数情况使用几次,可以简化对问题的处理。如果把调味拿来当饭吃,一定会本末倒置,写出的程序会呈现营养不良之状。
    事实上,longjmp和setjmp玩得熟不熟与是不是C语言高手,不是因果关系。但是,如果可以套用那位高手的话,我倒想说如果函数指针玩得不熟,就不要自称为C语言高手。为什么这么说呢,函数指针有那么复杂吗?
    当然不是,任何一个稍有编程常识的人,不管他懂不懂C语言,在10分钟内,我想他一定可以明白C语言中的函数指针是怎么回事。
    原因在于,难的不是函数指针的概念和语法本身,而是在什么时候,什么地方该使用它。函数指针不仅是语法上的问题,更重要的是它是一个设计范畴。
    真正的高手当然不单应该懂得语法层面上的技巧,更应该懂得设计上的方法。不懂设计,能算高手吗?怀疑我在夸大其辞吗?那我们先看看函数指针与哪些设计方法有关:与分层设计有关。分层设计早就不是什么新的概念,分层的好处是众所周知的,比较明显好处就是简化复杂度、隔离变化。
    采用分层设计,每层都只需关心自己的东西,这减小了系统的复杂度,层与层之间的交互仅限于一个很窄的接口,只要接口不变,某一层的变化不会影响其它层,这隔离了变化。
    分层的一般原则是,上层可以直接调用下层的函数,下层则不能直接调用上层的函数。这句话说来简单,在现实中,下层常常要反过来调用上层的函数。
    比如你在拷贝文件时,在界面层调用一个拷贝文件函数。界面层是上层,拷贝文件函数是下层,上层调用下层,理所当然。但是如果你想在拷贝文件时还要更新进度条,问题就来了。
    一方面,只有拷贝文件函数才知道拷贝的进度,但它不能去更新界面的进度条。另外一方面,界面知道如何去更新进度条,但它又不知道拷贝的进度。怎么办?
    常见的做法,就是界面设置一个回调函数给拷贝文件函数,拷贝文件函数在适当的时候调用这个回调函数来通知界面更新状态。
    与抽象有关。抽象是面向对象中最重要的概念之一,也是面向对象威力强大之处。面向对象只是一种思想,大家都知道,用C语言一样可以实现面向对象的编程。
    这可不是为了赶时髦,而是一种实用的方法。如果你对此表示怀疑,可以去看看GTK+、linux kernel等开源代码。
    接口是最高级的抽象。在linux kernel里面,接口的概念无处不在,像虚拟文件系统(VFS),它定义一个文件系统的接口,只要按照这种接口的规范,你可以自己开发一个文件系统挂上去。
    设备驱动程序更是如此,不同的设备驱动程序有自己一套不同的接口规范。在自己开发设备开发驱动程序时,只要遵循相应的接口规范就行了。接口在C语言中如何表示?很简单,就是一组函数指针。
    与接口与实现分开有关。针对接口编程,而不是针对实现编程,此为《设计模式》的第一条设计准则。分开接口与实现的目标是要隔离变化。软件是变化的,如果不能把变化的东西隔离开来,导致牵一发而动全身,代价是巨大的。这是大家所不愿看到的。
    C语言既然可以实现面向对象的编程,自然可以利用设计模式来分离接口与实现。像桥接模式、策略模式、状态模式、代理模式等等,在C语言中,无一不需要利用函数指针来实现。
与松耦合原则有关。面向过程与面向对象相比,之所以显得苍白无力,原因之一就是它不像面向对象一样,可以直观的把现实模型映射到计算机中。
    面向过程讲的是层层控制,而面向对象更强调的对象间的分工合作。现实世界中的对象处于层次关系的较少,处于对等关系的居多。也就是说,对象间的交互往往是双向的。这会加强对象间的耦合性。
    耦合本身没有错,实际上耦合是必不可少的,没有耦合就没有协作,对象之间无法形成一个整体,什么事也做不了。关键在于耦合要恰当,在实现预定功能的前提下,耦合要尽可能的松散。这样,系统的一部分变化对其它部分的影响会很少。
    函数指针是解耦对象关系的最佳利器。Signal(如boost的signal和glib中的signal)机制是一个典型的例子,一个对象自身的状态可能是在变化的(或者会触发一些事件),而其它对象关心它的变化。一旦该对象有变化发生,其它对象要执行相应的操作。
    如果该对象直接去调用其它对象的函数,功能是完成了,但对象之间的耦合太紧了。如何把这种耦合降到最低呢,signal机制是很好的办法。
    它的原理大致如下:其它关注该对象变化的对象主动注册一个回调函数到该对象中。一旦该对象有变化发生,就调用这些回调函数通知其它对象。功能同样实现了,但它们之间的耦合度降低了。
    在C语言中,要解决以上这些问题,不采用函数指针,将是非常困难的。

使用特权

评论回复
沙发
Henryko| | 2023-9-11 21:22 | 只看该作者
C如何实现面向对象啊

使用特权

评论回复
板凳
MessageRing| | 2023-9-14 23:53 | 只看该作者
Henryko 发表于 2023-9-11 21:22
C如何实现面向对象啊

就实现它的三大特性就行了

使用特权

评论回复
地板
Jacquetry| | 2023-9-16 23:17 | 只看该作者
Henryko 发表于 2023-9-11 21:22
C如何实现面向对象啊

可以看看这个帖子https://zhuanlan.zhihu.com/p/338267632

使用特权

评论回复
5
tpgf| | 2023-10-9 16:42 | 只看该作者
在单片机c语言中真的很少用指针  除了数组以外的哈

使用特权

评论回复
6
drer| | 2023-10-9 16:57 | 只看该作者
如果这么定义的话 那大部分人应该都不是高手啊

使用特权

评论回复
7
wiba| | 2023-10-9 17:25 | 只看该作者
其实我们使用的很多数据类型都是变相的指针数据

使用特权

评论回复
8
Undshing| | 2023-10-9 20:58 | 只看该作者
c也可以用面向对象的

使用特权

评论回复
9
Henryko| | 2023-10-10 13:08 | 只看该作者
有什么地方是非指针不可的啊

使用特权

评论回复
10
kxsi| | 2023-10-10 16:16 | 只看该作者
想要做一个真正的c语言高手可不是那么容易的

使用特权

评论回复
11
coshi| | 2023-10-10 16:32 | 只看该作者
其实也不是不能解决的 但是就是会比较甚至是非常的复杂

使用特权

评论回复
12
qcliu| | 2023-10-10 17:29 | 只看该作者
指针是c语言的精华  用好了非常不容易

使用特权

评论回复
13
Stahan| | 2023-10-11 20:50 | 只看该作者
用多了指针确实顺手啊

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

376

主题

1418

帖子

1

粉丝