今日听说某君批评 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. Java多线程笔记总结

    1.线程的三种创建方式 对比三种方式: 通过继承Thread类实现 通过实现Runnable接口 实现Callable接口 第1种方式无法继承其他类,第2,3种可以继承其他类: 第2,3种方式多线程可 ...

  2. android 界面提示框架WisdomProgressHUD,为金典而生

    一:简述 今天给android开发者们,推荐一个金典的界面提示框架WisdomProgressHUD,使用简洁方便. WisdomProgressHUD 是一个半透明的 HUD 指示器. Wisdom ...

  3. 原创:用node.js搭建本地服务模拟接口访问实现数据模拟

    前端开发中,数据模拟是必要的,这样就能等后台接口写完,我们直接把接口请求的url地址从本地数据模拟url换成后台真实地址就完成项目了.传参之类的都不用动. 之前网上找了很多类似于mock等感觉都不太实 ...

  4. 在WPF中嵌入WebBrowser可视化页面

    无论是哪种C/S技术,涉及数据可视化就非常的累赘了,当然大神也一定有,只不过面向大多数人,还是通过网页来实现,有的时候不想把这两个功能分开,一般会是客户的原因,所以我们打算在WPF中嵌入WebBrow ...

  5. PHP验证身份证格式

    互联网公司对身份证验证的需求越来越多,然而普通的小公司是无法对接公安部门的身份认证系统的.几乎都是在网上买一些大的互联网公司的一些认证服务.即使是便宜一些的认证价格也达到了10万次/万元.也就是一角钱 ...

  6. FTP服务端部署

    FTP服务端搭建(本地用户登入:使用本地用户和密码登入)1.文件配置:vsftpd.conf: 主配置文件ftpusers: 指定哪些用户不能访问FTP服务器user_list: 指定的用户是否可以访 ...

  7. React 基于antd+video.js实现m3u8格式视频播放及实时切换

    文档连接地址(官网看起麻烦,看中文别人整理好的)https://blog.csdn.net/a0405221/article/details/80923090 React项目使用  安装依赖 npm ...

  8. 1和new Number(1)有什么区别

    1和new Number(1)有什么区别 author: @Tiffanysbear 总结,两者的区别就是原始类型和包装对象的区别. 什么是包装对象 对象Number.String.Boolean分别 ...

  9. python3学习-pickle模块

    pickle提供了一个简单的持久化功能.可以将对象以文件的形式存放在磁盘上. 基本接口: pickle.dump(obj, file, [,protocol]) 注解:将对象obj保存到文件file中 ...

  10. EXP查询合集提权后渗透必备

    0x00 整理的一些后渗透提权需要用到的一些漏洞,后渗透提权的时候可以看一下目标机那些补丁没打,再进行下一步渗透提权. 0x01 CVE-2019-0803 [An elevation of priv ...