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);
    }
    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 C 语言中是无法直接传递闭包的,但是可以通过使用函数指针来模拟闭包的行为。在 Rust 中,可以使用 `extern "C"` 修饰符将 Rust 函数导出为 C 函数,然后将其作为参数传递给 C 函数。 下面是一个使用原始指针传递闭包作为 C 函数参数的示例代码: ```rust // 定义一个闭包类型 type Callback = Box<dyn FnMut()>; // 将闭包转换为原始指针 fn callback_to_ptr(callback: Callback) -> *mut std::ffi::c_void { Box::into_raw(callback) as *mut std::ffi::c_void } // 将原始指针转换为闭包 unsafe fn ptr_to_callback(ptr: *mut std::ffi::c_void) -> Callback { Box::from_raw(ptr as *mut dyn FnMut()) } // C 函数,接受一个指向闭包的原始指针作为参数 extern "C" fn call_closure(ptr: *mut std::ffi::c_void) { unsafe { // 将原始指针转换为闭包 let mut callback = ptr_to_callback(ptr); // 调用闭包 callback(); // 将闭包转换回原始指针,以防止其被 Rust 的垃圾回收器释放 callback = callback_to_ptr(callback); } } fn main() { // 定义一个闭包 let mut closure = || { println!("Hello from closure!"); }; // 将闭包转换为原始指针 let ptr = callback_to_ptr(Box::new(closure)); // 调用 C 函数,并传递闭包的原始指针作为参数 unsafe { call_closure(ptr); } } ``` 在上面的示例代码中,我们首先定义了一个闭包类型 `Callback`,然后分别实现了将闭包转换为原始指针和将原始指针转换为闭包函数。接下来,我们定义了一个 C 函数 `call_closure`,它接受一个指向闭包的原始指针作为参数。在 `main` 函数中,我们定义了一个闭包,并将其转换为原始指针。然后,我们调用了 C 函数 `call_closure`,并将闭包的原始指针作为参数传递给它。在 `call_closure` 函数中,我们将原始指针转换为闭包,并调用了它。 需要注意的是,在将闭包转换为原始指针时,我们使用了 `Box::into_raw` 函数。这个函数会将 `Box` 类型的对象转换为指向堆内存的原始指针,并释放 `Box` 对象的所有权。在将原始指针转换为闭包时,我们使用了 `Box::from_raw` 函数。这个函数会将指向堆内存的原始指针转换为 `Box` 类型的对象,并获取 `Box` 对象的所有权。需要注意的是,在使用 `Box::from_raw` 函数时,必须确保传递的指针是由 `Box::into_raw` 函数产生的。否则,可能会导致内存安全问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值