C 语言中统一的函数指针

标签: 语言与设计 | 发表时间:2010-07-13 20:49 | 作者:云风 kezhuw
出处:http://blog.codingnow.com/

有时候,我们需要把多个模块粘合在一起。而这些模块的接口参数上有少许的不同。在 C 语言中,参数(或是返回值)不同的函数指针属于不同的类型,如果混用,编译器会警告你类型错误。

在 C 语言中,函数定义是可以不写参数的。比如:

void foo();

这个函数定义表示了一个返回 void 的函数,参数未定。也就是说,它是个弱类型,诸如:

void foo(int);

void foo(void *);

这些类型都可以无害的转换成它。正如在 C 语言中,具体的指针类型如 int * ,char * 都可以转换为 void * 一样。

注1:如果要严格定义一个无参数的函数,应该写成 void foo(void);

注2:如果有部分参数固定,而其后的参数可变,则定义看起来是这样: void foo(int , ...); 这表示第一个参数为 int ,从第 2 个参数开始可变。

不过,C 语言的这种语法,实际上不太使用。因为用 C 语言无法主动控制函数调用的参数压栈。我们很难根据程序的上下文来决定如何传入参数去调用某个函数。如果需要逐级传递多个函数的参数,用的更多的是 va_list

比如,你很难对 printf 做封装,通常为了方便做封装,还提供了形为 vprintf 的接口。

C++ 解决此类问题的方案是用类去模拟一个函数,通过重载 () 操作符的方法,让函数调用看起来和普通函数一致(并美其名曰 functor/仿函数)。当然,也有撕破语法糖的伪装,用更直白的类继承的方式来定义出接口。

这里想说的是,C 语言里也还有一种有趣的方案来在保证类型安全的基础上解决类似问题。

在 X-Window 的消息定义中就可以看到这样的手法。

在 Windows 的接口中,Windows 的消息携带的数据通常用两个参数来表示:WPARAM 和 LPARAM ,均为 32bit 整数。我们知道,消息本质上等同于对象的方法。在更早的面向对象语言如 smalltalk 中,调用对象的方法即被看成向对象发送一个消息。Windows 如此把所有消息处理相关函数的接口都以 WPARAM 与 LPARAM 的形式传递参数,正是为了方便统一其接口形式。各种五花八门的参数都蕴涵于这 64 bit 数据中。

Xlib 处理类似的问题,对 C 程序员的亲合力则大的多。至少更为类型安全。

Xlib 定义了一个叫做 XEvent 的结构体(实际是一个 union)。然后把各种可能的消息类型放在这个 union 中。例如,我想取键盘消息,则可以用 event.xkey.keycode 。

一般说来,我们可以把模块的对外接口看成是接收一组输入参数并加以处理。如果需要粘合多个不同的模块,他们需要处理不同的输入参数的话,可以借鉴 XLib 的这个方法。在粘合层定义一个 union ,把所有可能的参数组,每组定义成一个 struct 然后定义在同一个 union 中。这个粘合层的统一接口则为这个 union 指针。有必要的话,所有的参数组 struct 的头部都留下 type 字段。这样比较容易分发消息。

这样做的本质是:把函数调用时由编译生成的、将调用参数逐个压栈的代码,改由程序员主动填写(填写参数结构体)。利用结构的类型安全,保证了函数调用时的参数类型安全。再利用 union 的语法,把不同的参数组联合到一起变成同一类型。

给 api 传递一个 struct 或 union 指针而不是逐个参数传递,是 C 接口设计的一种常见手法。除了 XLib 的设计,还能找到很多耳熟能详的例子。例如,我们在 socket api 上也可以看到类似的东西。例如 connect 的参数中有一 sockaddr 结构,就适用于各种不同的网络底层协议。

相关 [语言 中统 函数指针] 推荐:

C 语言中统一的函数指针

- kezhuw - 云风的 BLOG
有时候,我们需要把多个模块粘合在一起. 而这些模块的接口参数上有少许的不同. 在 C 语言中,参数(或是返回值)不同的函数指针属于不同的类型,如果混用,编译器会警告你类型错误. 在 C 语言中,函数定义是可以不写参数的. 这个函数定义表示了一个返回 void 的函数,参数未定. 也就是说,它是个弱类型,诸如:.

使用纯C函数指针调用C++的类成员函数

- 叽歪陈 - C++博客-首页原创精华区
出发点:之前偶然碰见一个需要使用C代码调用C++的成员函数的场景,当时在google上没有找到解决方案,于是记录下了这个需求,今天看了GECKO的NPAPI代码,找到一种方式. 原理:类的static成员是作为共享的方式被发布给外层的,所以不具有成员函数地址,所以它可以用来为我们转弯的调用类的成员函数提供一个机会.

语言壁垒

- kylexlau - Chun Tian (binghe)
虽然经常更新微博,但确实又有些日子没写常规博客了. 10 月开始前有必要再写一篇,整理一下近期的各种思路,以良好的精神面貌迎接今年的最后一个季度. 首先发布一个已经不太新的消息:Practical Common Lisp 的中文版《实用 Common Lisp 编程》几经周折终于付印了,下月即可正式出版;读者们甚至已经可以在当当网上预订了.

UnQL:NoSQL查询语言

- 卡飞菲 - Solidot
CouchDB作者Damien Katz与SQLite作者Richard Hipp正在为NoSQL文档数据库开发一种高级查询语言UnQL(发音同Uncle,是UNstructured Query Language的缩写). Katz表示,他们坚信为了推广NoSQL需要一种通用的查询语言,类似当年推广关系型数据库应运而生的SQL语言.

Go 语言初步

- wei - 云风的 BLOG
所谓认真玩,就是拿 Go 写点程序,前后大约两千行吧. 据说 Go 的最佳开发平台是 Mac OS ,我没有. Windows 版还没全部搞定,但是也可以用了. 如果你用 google 搜索,很容易去到一个叫 go-windows 的开源项目上. 如果你用这个,很多库都没有,而且语法也是老的. 我在 Windows 下甚至不能正确链接自己写的多个 package.

C语言之父

- Dylen - 子说
同事说c语言之父去世了,另一个同事大吃一惊,问:谭浩强死了. 不知丹尼斯·里奇(Dennis Ritchie)情何以堪.

如何学好C语言

- 夕角 - 酷壳 - CoolShell.cn
有人在酷壳的留言版上询问下面的问题. 我也遇到了和提问的老外一样的问题. 能给像遇到这样烦恼的程序员一点建议嘛. 我相信,这可能是很多朋友的问题,我以前也有这样的感觉,编程编到一定的时候,发现能力到了瓶颈,既不深,也不扎实,半吊子. 比如:你长期地使用Java和.NET ,这些有虚拟机的语言对于开发便利是便利,但是对于程序员来说可能并不太好,原因有两个:.

笨办法学C语言

- Hexi - Solidot
继《笨办法学Python》之后,Zed Shaw开始动手写《笨办法学C语言》. 和《笨办法学Python》一样,这本C语言入门书也将采用习题方式引导读者学习. 在序言中,他引用了笛卡尔《第一哲学沉思录》中的一段话去描述C语言:“直到现在,凡是我当作最真实、最可靠而接受过来的东西,我都是从感官或通过感官得来的.

C语言编译器 Cilk

- Le - 开源中国社区最新软件
Cilk多线程编程技术最早由MIT开 发,是一个基于Gcc编译器的开源项目. 后来开发者创建了一个创业公司,推出改进的私有版本,整合到Windows下的多种编译器中. 之后它被英特尔公司 收购,整合进英特尔的编译器中. 现在,它再次成为一个开源项目,成为Gcc 4.7下的一个分支. Cilk Plus允许C和C++程序员能高效利用多核处理器的并行处理能力.

Java7新语言特性

- zffl - Import
Java7发布有一段时间了,这几天升级了一下JDK,结果发现Eclipse3.7还没有支持JDK7. 这个需要稍微解释一下,可能有人不知道,Eclipse使用自己的Java编译器而不是JDK自带的javac. Eclipse自己的编译器就是ecj(Eclipse Compiler for Java),一般的Linux发行版都有eclipse-ecj这样的包.