C函数指针的偏门作用

Friday, August 22nd, 2008 143 views

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),实际上起作用的是这样一个有着冗长名字的函数。

函数指针这种避免命名冲突上的额外好处对于一些库函数的编写者是很有意义的,因为库可能被很多的用户在许多不同的环境下使用,这样就能有效的避免冲突而保证库的可用性。

认领 BANGD2569BE2B2E8A4BC39F1189CXIANGUO

C与C++在函数指针上的区别

Thursday, August 21st, 2008 154 views

最近为C语言写一些数据结构相关的库,遇到一个有趣的问题。我最初版本的代码能够在GCC下用纯C编译通过,但是却不能用C++编译通过,尝试用Visual C++6.0编译,也是报一样的错。我还一直以为C++能够完全的兼容C呢,看来观点需要变化了。
下面详细说明下。

我定义了一个stack结构体(最初的定义)如下:

1
2
3
4
5
6
7
8
9
typedef struct easyc_stack{
               int inc_size;
               int point;
               int * base;
               int size;
               int  (*pop)();
               int  (*push)();
               int  (*get_top)();
        }c_stack;

在另外的代码里我声明了一个函数

1
int ugly_easyc_pop(c_stack *);

然后编写一个stack创建函数

1
2
3
4
5
6
7
c_stack make_stack(int size){
    c_stack s;
    .....//some code
    s.pop=ugly_easyc_pop;
    .....//some code
    return s;
}

在纯C编译器中,这样的做法毫无问题,经过测试,push, pop也能正常工作。
但是在用C++编译器编译就出问题了,看错误信息是类型不匹配,C++编译器认为 int (*)()与 int (*)(c_stack *)是不一样的类型,而C编译器觉得无所谓。因此,要在C和C++中都正常编译,需要修改c_stack中函数指针的定义,修改如下:

1
2
3
4
5
6
7
8
9
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;

我猜C++这样做的原因可能是因为它支持函数的overload,因而,同函数名,不同的参数被认为是不同的类型了。
认领 BANGD2569BE2B2E8A4BC39F1189CXIANGUO

关于

我叫陈炬,正在天津大学计算机学院->知识工程与科学研究所攻读硕士研究生。今年应届。您可以在我的自我介绍获得更多关于我的信息

订阅我的Blog


本站RSS地址| [这是什么?]
订阅到Google Reader | 订阅到 抓虾 阅读器 | 订阅到 鲜果 阅读器

其他blog

我还有一个英文blog,非常的无聊,琐碎,许多的语法错误。请谨慎访问。
Find entries :