原书P57:static void(* set_malloc_handler(void (*f)()))(){...} 的理解

89 阅读1分钟

  今日阅读《STL源码剖析》至2.2节P57时,遇到令我感到困惑的一段代码,见下。

static void(* set_malloc_handler(void (*f)()))(){
    void (*old)() = __malloc_alloc_oom_handler;
    __malloc_alloc_oom_handler = f;
    return (old);
}

  简单讲一下这段代码的作用:其中 __malloc_alloc_oom_handler\_\_malloc\_alloc\_oom\_handler 是一个函数指针,声明如下:

static void (* __malloc_alloc_oom_handler)();

  用于指向处理出现 out of memory(oom)out \ of \ memory(oom) 时调用的处理函数。这段函数接收一个指向自定义的 out of memory handlerout \ of \ memory \ handler 处理函数的函数指针 ff,在函数内部先保存 __malloc_alloc_oom_handler\_\_malloc\_alloc\_oom\_handler 记录的旧值 oldold 然后将其赋值为 ff。最后将旧值 oldold 返回。

  理解函数的逻辑并不困难,但是函数的声明形式有待理解。什么是函数指针呢?下面讲解。

函数指针

  请注意区分函数指针指针函数。我们知道,在c++中,存在包括基本数据类型在内的数据类型,事实上,函数同样存在不同的类型——函数的类型由其返回值以及形参决定,这两者称之为函数特征(符)。和其他数据类型可以通过指针进行访问一样,函数同样可以用特殊的指针进行间接的调用。相对于其他数据类型,指针指向的是其地址,而函数指针指向的是函数代码的地址

函数指针的声明

  函数指针的声明格式通常如下:

[返回值类型](指针变量名)(参数类型)[返回值类型](* 指针变量名)(参数类型)

  例如:

void (*func)();  // 声明一个**无返回值**且**参数列表为空**的函数指针
int (*func)(int, int);  // 声明一个返回值类型为 int,参数列表类型均为 int 的函数指针
...

// 通过函数指针调用函数:
int a = (*func)(1,2);
// 或
int b = func(1, 2);

  在理解上面这个问题之前,首先来看一个非常典型的例子:

(* (void (*)()) 0)();  //出自经典书籍《C Trap and Pitfalls》

  如何理解?不妨逐层探之。首先 void ()()void \ (*)() 是一个无返回值且参数列表为空的函数指针类型,于是可知, (void ()())0(void \ (*)()) 0 强制将 00 转化为函数指针,然后进行调用。

释解

  有了上面函数指针的基础,我们便可以对问题进行解释了:

static void(* set_malloc_handler(void (*f)()))(){
    void (*old)() = __malloc_alloc_oom_handler;
    __malloc_alloc_oom_handler = f;
    return (old);
}

  同样,由内而外逐层进行理解。首先 void (*f)() 是一个无返回值且参数列表为空的函数指针。既然如此,那么 set_malloc_handler(void (*f)()) 应该是一个函数,形参类型是一个函数指针 void (*f)()

  如果将 set_malloc_handler(void (*f)()) 视为一个整体 P,可以得到 void (* P)(),同样也是一个无返回值,参数列表为空的函数指针。然而注意到 set_malloc_handler(void (*f)()) 前是 * ,说明函数 set_malloc_handler(void (*f)()) 返回一个指针。而这个指针也有自己的参数列表(为空),以及返回值类型为 void。于是可知 set_malloc_handler(void (*f)()) 的返回类型是无返回值,参数列表为空的函数指针。

  事实上,上面的函数有更易读的等价写法:

typedef FuncType void (*)();
Functype set_malloc_handler(void (*f)()){
    ...;
}