使用GCC语句表达式的匿名函数

Bil*_* VB 49 c lambda gcc anonymous-function

这个问题并不十分具体; 这真的是我自己的C浓缩,我希望其他人也能发现它有用.

免责声明:我知道很多人会有回应的冲动"如果你试图做FP然后只使用一种功能语言".我在一个需要链接到许多其他C库的嵌入式环境中工作,并且没有太多空间用于更多大型共享库,并且不支持许多语言运行时.而且,动态内存分配是不可能的.我也很好奇.

我们中的许多人已经看到了lambda表达式的这个漂亮的C宏:

#define lambda(return_type, function_body) \
({ \
      return_type __fn__ function_body \
          __fn__; \
})
Run Code Online (Sandbox Code Playgroud)

一个示例用法是:

int (*max)(int, int) = lambda (int, (int x, int y) { return x > y ? x : y; });
max(4, 5); // Example
Run Code Online (Sandbox Code Playgroud)

使用gcc -std=c89 -E test.c,lambda扩展为:

int (*max)(int, int) = ({ int __fn__ (int x, int y) { return x > y ? x : y; } __fn__; });
Run Code Online (Sandbox Code Playgroud)

所以,这些是我的问题:

  1. 线int(*X)究竟是什么?宣布?当然,int*X; 是指向整数的指针,但这两者有何不同?

  2. 看一下这个扩大的宏观,最终会__fn__做什么呢?如果我写一个测试函数void test() { printf("hello"); } test;- 立即抛出错误.我不明白那种语法.

  3. 这对调试意味着什么?(我打算尝试自己和gdb,但其他人的经验或意见会很棒).这会搞乱静态分析仪吗?

oua*_*uah 39

此声明(在块范围内):

int (*max)(int, int) =
    ({
    int __fn__ (int x, int y) { return x > y ? x : y; }
    __fn__;
    });
Run Code Online (Sandbox Code Playgroud)

不是C而是有效的GNU C.

它使用了两个gcc扩展:

  1. 嵌套函数
  2. 语句表达式

两个嵌套函数(限定的复合语句内的函数)和声明表达式(({}),基本上是产生一个值的块)用C不允许和来自GNU C.

在语句表达式中,最后一个表达式语句是构造的值.这就是嵌套函数__fn__在语句表达式末尾显示为表达式语句的原因.__fn__表达式中的函数指示符(在最后一个表达式语句中)通过常规转换转换为指向函数的指针.这是用于初始化函数指针的值max.

  • 这样一个有用的功能,什么时候会在ANSI C中实现? (4认同)
  • 它在语法上是有效的GNU C,但在语义上确实有效吗?当包含函数定义的块退出时(这可能在“ lambda表达式”的任何实际使用之前发生),函数蹦床会不会随之失效? (2认同)
  • @BradenBest:它当然没有静态持续时间,因为指向嵌套函数的指针在包含函数返回后无效。由于嵌套函数可以访问其范围内词法上的所有变量,因此如果它的蹦床分配的持续时间与这些变量相同,那就不会奇怪了。不过,该手册在这个问题上有点不清楚。 (2认同)

Mar*_*erg 6

你的lambda宏利用了两个时髦的功能.首先,它使用嵌套函数来实际定义函数体(因此你的lambda实际上并不是匿名的,它只使用一个隐式__fn__变量(应该重命名为其他变量,因为双引导下划线名称是为编译器保留的,所以也许yourapp__fn__会有更好的事情).

所有这些本身都是在GCC复合声明中执行的(参见http://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html#Statement-Exprs),其基本格式如下:

({ ...; retval; })
Run Code Online (Sandbox Code Playgroud)

复合语句的最后一个语句是刚刚声明的函数的地址.现在,int (*max)(int,int)只需赋予复合语句的值,该语句现在是指向刚刚声明的"匿名"函数的指针.

调试宏当然是一种皇家的痛苦.

至于为什么test;..至少在这里,我得到'测试重新声明为不同类型的符号',我认为这意味着GCC将其视为声明而不是(无用)表达.因为无类型变量默认为int并且因为你已经声明test为函数(本质上void (*)(void)),所以你得到了......但我可能错了.

然而,任何想象力都无法携带.