天天看点

c 语言函数指针和高级语言闭包区别

高阶函数

所谓高阶函数,就是用函数作为参数的函数。

函数指针

typedef void (*func_t)(int);
void foreach(struct node *list, func_t func) {
		while (list) { 
      func(list->val); 
      list = list->next;
    }
}
void f(int n) {
  	printf("node(?) = %d\n", n);
}
int main() {
  	struct node *list = 0;
  	foreach(list, f);
}
           
  1. 受到 C 语言语法的制约,这个函数必须在远离使用的地方单独进行定义。
  2. foreach 的循环实际上是在另一函数中执行的,因此无法从函数中访问位于外部的局部变量 i。当然,如果变量 i 是一个全局变量就不存在这个问题了,不过为了这个目的而使用副作用很大的全局变量也并不是一个好主意。“对外部(局部)变量的访问”是 C 语言 函数指针的最大弱点。

由于 JavaScript 中可以直接定义函数对象,作为 foreach 参数的函数对象,是可以访问在外部声明的变量 i 的。、

结 果,C 语言版的 foreach 函数无法实现的索引显示功能,在这里就可以实现了。因此,从函 数对象中能够对外部变量进行访问(引用、更新),是闭包的构成要件之一。

function foreach(list, func) { 
  while (list) { 
    func(list.val); 
    list = list.next; 
  } 
}

var i = 0; // i 初始化
// 从函数对象中访问外部变量
foreach(list, function(n){console.log("node("+i+") = "+n);i++;});
           

extent 这个函数的返回值是一个函数对象。函 数对象会对 extent 中的一个局部变量 n 进行累加,并显示它的值。

function extent() {
	var n = 0; // 局部变量
	return function() { n++; console.log("n="+n); }
	// 对 n 的访问
} 
f = extent(); // 返回函数对象
f();  // n=1 
f();  // n=2
           

这个从属于外部作用域中的局部变量,被函数对象给“封闭” 在里面了。闭包(Closure)这个词原本就是封闭的意思。被封闭起来的变量的寿命,与封 闭它的函数对象寿命相等。为什么嗯,因为它是对象,对象分配在堆中,只有这个变量的函数对象被回收了,这个变量才生存周期才结束。

总结:

  1. c语言的函数指针并不是闭包,JavaScript的函数对象才是闭包。
  2. 闭包是跟语言的特性相关的,c语言没有对象的概念,如果c语言使用结构体保存变量和函数指针,是否可以实现闭包呢?我做了以下尝试。
    #include <stdio.h>
    #include <stdlib.h>
    
    typedef struct closure_s{
        int x;
        void (*call)(struct closure_s*);
    } closure;
    
    void f(closure *clo) {
        clo->x += 1;
        printf("node = %d\n", clo->x);
    }
    
    closure *extent() {
        closure *func = (closure *)malloc(sizeof(closure));
        func->x = 0;
        func->call = f; 
        return func;
    }
    
    int main() {
        closure *clo = extent();
        clo->call(clo);			// node = 1
        clo->call(clo);			// node = 2
        free(clo);
    }
               

继续阅读