C语言的指针不仅可以指向数据域,也可以指向一个函数。很多C的初学者并不清楚这个特性,在看一些源代码的时候常常会觉得困惑。
C的函数最常用的是提供函数Callback的能力,比如,C的”stdlib”中声明的qsort函数,用来对数值进行排序。显然,顺序还是降序,元素谁大谁小这些问题,库程序员在编写qsort的时候不可能决定。这些问题是要在用户调用这个函数的时候才能够决定。那边qsort如何保证通用性和灵活性呢?采用的办法是让函数的使用者来制定排序规则。qsort的声明如下:
void qsort ( void * base, size_t num, size_t size, int ( * comparator ) ( const void *, const void * ) );
其中
int ( * comparator ) ( const void *, const void * )
便是一个函数指针,指向某个用户自定义的比较函数。参考C++ Reference的例子如下:
/* qsort example */ int values[] = { 40, 10, 100, 90, 20, 25 }; int compare (const void * a, const void * b) { return ( *(int*)a - *(int*)b ); } int main () { int n; qsort (values, 6, sizeof(int), compare); for (n=0;n<6; n++) printf ("%d ",values[n]); return 0; }
除了提供回调的能力之外,C函数指针也有其他有趣的用途,比如,利用它来进行面向对象式的编程,比如,利用它来避免C名字空间的冲突。
参考上文,我提供的stack结构体的定义如下
typedef struct easyc_stack{ int base_size; int point; int * base; int size; int (*pop)(struct easyc_stack *); int (*push)(int,struct easyc_stack *); int (*get_top)(struct easyc_stack ); }c_stack;
在初始化完之后,用户调用这个结构体上的pop函数,只需要 s.pop(&s)即可。即使这个时候,工程内部有另外一个函数名字也叫pop,他们之间是不会发生名字上的冲突的。原因很简单,因为结构体中的函数指针pop指向的函数名字可能是
int ugly_stupid_no_one_will_use_this_name_pop(c_stack *)
,只是stack的用户是不知道他在调用s.pop(&s),实际上起作用的是这样一个有着冗长名字的函数。
函数指针这种避免命名冲突上的额外好处对于一些库函数的编写者是很有意义的,因为库可能被很多的用户在许多不同的环境下使用,这样就能有效的避免冲突而保证库的可用性。