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

时间:2012-05-01 22:45:36

标签: c gcc lambda anonymous-function

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

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

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

#define lambda(return_type, function_body) \
({ \
      return_type __fn__ function_body \
          __fn__; \
})

示例用法是:

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

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

int (*max)(int, int) = ({ int __fn__ (int x, int y) { return x > y ? x : y; } __fn__; });

所以,这些是我的问题:

  1. int(* X); 的具体内容是什么?当然, int * X; 是一个指向整数的指针,但这两者有何区别?

  2. 看一下exapnded macro,最终的__fn__到底是做什么的?如果我编写一个测试函数void test() { printf("hello"); } test; - 会立即抛出错误。我不明白这种语法。

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

4 个答案:

答案 0 :(得分:38)

此声明(在块范围内):

int (*max)(int, int) =
    ({
    int __fn__ (int x, int y) { return x > y ? x : y; }
    __fn__;
    });

不是C,但是有效的GNU C.

它使用了两个gcc扩展程序:

  1. nested functions
  2. statement expressions
  3. 不允许嵌套函数(在复合语句中定义函数)和语句表达式({}),基本上是一个产生值的块)在C中来自GNU C。

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

答案 1 :(得分:6)

你的lambda宏利用了两个时髦的功能。首先它使用嵌套函数实际定义函数体(所以你的lambda实际上不是匿名的,它只使用一个隐式的__fn__变量(应该重命名为其他变量,如双引导下划线名称是为编译器保留的,所以像yourapp__fn__这样的东西会更好。)

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

({ ...; retval; })

复合语句的最后一个语句是刚刚声明的函数的地址。现在,int (*max)(int,int)只被赋予复合语句的值,现在是指向刚声明的'anonymous'函数的指针。

调试宏当然是一种巨大的痛苦。

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

尽管如此,这在任何想象中都是不可移植的。

答案 2 :(得分:1)

部分答案: 它不是你感兴趣的int(* X)。它是int(* X)(y,z)。这是一个函数指针,指向名为X的函数,它接受(y,z)并返回int。

对于调试,这将非常困难。大多数调试器无法跟踪宏。您很可能必须调试程序集。

答案 3 :(得分:0)

  1. int (*max)(int, int)是您要声明的变量类型。它被定义为一个名为max的函数指针,它返回int,并将两个整数作为参数。

  2. __fn__是指函数名称,在本例中为max。

  3. 我在那里没有答案。如果你通过预处理器运行它,我想你可以逐步完成它。