今日听说某君批评 C 语言说它【输入一个参数返回一个函数】很困难。

例如在 Python 中,你可以

def addn(n):
def addx(x):
return n + x
return addx my_adder = addn(42)
print(my_adder(21))

标准 C 要实现这样的功能也不是不可以,这是一个闭包的功能,我们只要显式的把上下文抓住就可以了。

#include <stdio.h>

typedef struct Adder { int n; } Adder;

Adder addn(int n) {
Adder addn;
addn.n = n;
return addn;
} int apply(Adder addn, int x) { return addn.n + x; } int main() {
Adder add42 = addn(42);
printf("%d\n", apply(add42, 21)); return 0;
}

这样的写法未免过于复杂,而闭包是现代程序设计里面很常用到的一种结构,面对更复杂的上下文,为了模拟闭包,所要做的比一个 Adder 结构体要多得多。对于这样常用的结构,主流的 C 编译器,即 gcc 和 clang 都提供了 C 扩展来支持这样的功能。

在 clang 中,你可以这样写

#include <stdio.h>
#include <Block.h> int (^addn(int n))(int) {
__block int i = n; return Block_copy( ^(int x) {
return x + n;
});
} int main() {
int (^add42)(int) = addn(42);
printf("%d\n", add42(21)); return 0;
}

熟悉 Objective-C 的同学应该对 block 不陌生,这里 block 的类型名写起来比较麻烦,可以用 typedef int (^MyClosure)(int); 来定义更有可读性的类型名。

在 gcc 中,可以使用 nested functions 扩展

#include <stdio.h>

int (*addn(int n))(int) {
int addx(int x) {
return n + x;
} return addx;
} int main() {
int (*add42)(int) = addn(42);
printf("%d\n", add42(21)); return 0;
}

同样这里的函数指针也可以用 typedef 来定义一个可读的类型名。

这个问题在 C++ 中可以通过在类中重载括号运算符来实现。本质上是上面复杂代码所揭示出的捕获上下文并查看上下文信息的动作,现代语言为此封装了看起来像直接调用的简洁语法,使得代码更好写,更好读。

C 扩展对闭包特性的支持的更多相关文章

  1. 目前主流编译器对C++11特性的支持情况

    目前主流编译器对C++11特性的支持情况 1. GCC编译器(从编译器GCC4.8.X的版本完全支持) (1)目前C++11特性,之前成为C++0X特性,从GCC4.3的后续版本中逐步对C++11进行 ...

  2. 深入理解JavaScript的闭包特性如何给循环中的对象添加事件

    初学者经常碰到的,即获取HTML元素集合,循环给元素添加事件.在事件响应函数中(event handler)获取对应的索引.但每次获取的都是最后一次循环的索引.原因是初学者并未理解JavaScript ...

  3. 快速入门系列--WCF--08扩展与新特性

    最后一章将进行WCF扩展和新特性的学习,这部分内容有一定深度,有一个基本的了解即可,当需要自定义一个完整的SOA框架时,可以再进行细致的学习和实践. 服务端架构体系的构建主要包含接下来的几个要素:服务 ...

  4. Datagrid扩展方法onClickCell{easyui-datagrid-扩充-支持单元格编辑}

    //-----------------------------------------------------------------/******************************** ...

  5. 如何给循环中的对象添加事件--深入理解JavaScript的闭包特性

    初学者经常碰到的,即获取HTML元素集合,循环给元素添加事件.在事件响应函数中(event handler)获取对应的索引.但每次获取的都是最后一次循环的索引.原因是初学者并未理解JavaScript ...

  6. 深入理解JavaScript的闭包特性 如何给循环中的对象添加事件(转载)

    原文参考:http://blog.csdn.net/gaoshanwudi/article/details/7355794 初学者经常碰到的,即获取HTML元素集合,循环给元素添加事件.在事件响应函数 ...

  7. JavaScript的闭包特性

    闭包是一个比较抽象的概念,尤其是对js新手来说.在这里,我就我个人的理解j简单谈一下: 闭包:官方解释是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部 ...

  8. 从匿名函数(闭包特性)到 PHP 设计模式之容器模式

    匿名函数(匿名函数) 匿名函数,也叫闭包函数,它允许临时创建一个没有指定名称的函数,常用作回调函数参数的值,也可以作为变量的值来使用.具体的使用见以下示例代码: /* 示例一:声明一个简单匿名函数,并 ...

  9. JavaScript快速检测浏览器对CSS3特性的支持情况

    项目中使用动画效果在IE9下不支持,所以写了个判断浏览器是否支持动画的函数,进而扩展到下面判断浏览器支持任一特定CSS3属性的函数. function supportAnimation(){ var ...

随机推荐

  1. spring-Scheduler

    作者:纯洁的微笑出处:http://www.ityouknow.com/ 版权所有,欢迎保留原文链接进行转载:) 在我们的项目开发过程中,经常需要定时任务来帮助我们来做一些内容,springboot默 ...

  2. 2019全国大学生信息安全与对抗技术竞赛全国线下总决赛 Writeup

    0x00 Begin 关于 ISCC 2019 北理工总决赛,这一次比赛体验感总体差不多,最后我们战队荣获全国一等奖第一名,在这里非常感谢我的团队以及我的队友. 0x01 Reverse 下载题目:e ...

  3. 【iOS】Xcode 离线文档

    Xcode 本身下载太慢…… Apple 官方文档地址:https://developer.apple.com/library/downloads/docset-index.dvtdownloadab ...

  4. centos6.5-7编译安装Ansible详细部署

    一.基础介绍==========================================================================================ansi ...

  5. git之coding.net的使用

    先在Coding上创建个项目     现在是这样,我本地有个项目Project(/Users/huang/Desktop/Project),我想把它上传到刚创建的项目里,以后就用git代码托管.可我之 ...

  6. 挂起(suspend)与线程阻塞工具类LockSupport

    挂起(suspend)与线程阻塞工具类LockSupport 一般来说是不推荐使用suspend去挂起线程的,因为suspend在导致线程暂停的同时,并不会去释放任何锁资源. 如果其他任何线程想要访问 ...

  7. JAVA开始(基础篇)

    数据类型 Boolean        1位Byte              1个字节(8位)Short             2个字节Char              2个字节Int      ...

  8. .NET Core 3.0深入源码理解HttpClientFactory之实战

      写在前面 前面两篇文章透过源码角度,理解了HttpClientFactory的内部实现,当我们在项目中使用时,总会涉及以下几个问题: HttpClient超时处理以及重试机制 HttpClient ...

  9. 深入了解String和intern

    引言 在 JAVA 语言中有8中基本类型和一种比较特殊的类型String.这些类型为了使他们在运行过程中速度更快,更节省内存,都提供了一种常量池的概念.常量池就类似一个JAVA系统级别提供的缓存. 8 ...

  10. Android活动(Activity)创建及生命周期

       Activity是Android的门面,可以与用户进行互动的重要模块,凡是在应用中可以看到的东西,都是放在活动中的.   在学习新的技术时,我喜欢将需要学习的技术与自己懂得技术进行类似比较,而活 ...