为什么我可以在另一个函数中定义一个函数?

11
在下面的代码中,我在另一个函数中定义了一个函数:
void test1(void)
{
 void test2(void)
 {
   printf("test2\n");
 }
 printf("test1\n");
}

int main(void)
{
 test1();
 return 0;
}

这个用法很奇怪,它是c89/c99的用法还是gcc的扩展(我在ubuntu 12上使用gcc 4.6.3编译)。我运行了这段代码,它输出了“test2”和“test1”。只能在test1中调用test2。

此外,这个用法通常用于什么场景或者说被用来做什么?


3
它限制了 test2() 的作用范围,你无法在 test1() 之外调用它。 - bikram990
4个回答

18

是的,这是GCC扩展

这不是C语言,不具备可移植性,因此除非您知道GCC将:

  • 是构建代码所使用的唯一编译器
  • 将在未来版本中继续支持此功能
  • 不关心最少惊奇原则

1
当然,如果您使用C++11,基本上可以使用lambda定义嵌套函数。 - chris
2
@bjskishore123,auto test2 = []{printf("test2\n");}; 不会执行它,但仍然像函数定义一样起作用。 - chris
如果@unwind是gcc的扩展,那么它的使用目的是什么? - Ezio
@bjskishore123 我试过了,如果我在test1中不调用test2而只是定义它,test2将不会运行,当我对目标文件进行objdump时,发现如果不调用test2,test2的代码将被省略。 - Ezio
1
@CTMacUser:甚至不需要变量:[]{ return printf("test3\n"); }();。但这已经非常接近 Perl 了。 - MSalters
显示剩余5条评论

4
作为现有的代码,它不符合C++标准。但您可以在函数内定义一个类,并在该类中定义函数。即使如此,在C++11之前,这仍然仅限于词法嵌套。您定义的类不会"捕获"任何外部函数环境的上下文(除非您显式实现捕获)。而在真正的嵌套函数中,嵌套函数可以访问外部函数的局部变量。在C++11中,您可以定义具有自动捕获的lambda函数。
C和C++从未采用嵌套函数的原因是为了使捕获生效,需要额外的信息,从而使函数指针变得更加复杂。结果要么无法取嵌套函数的地址(缺乏正交性),要么指向嵌套函数的指针与普通函数指针不兼容(这最终需要太多外部详细信息才能传递出去),要么所有函数指针都有额外的信息(并且大多数情况下您付出代价,却用不到这些信息)。

2
@Marcus - "capture" 是意图,"closure" 是其中一种实现方式。只有编译器需要关心闭包,它可能会选择不同的实现方式(尽管闭包通常是很好的选择)。 - user180247
或许值得一提的是 - 嵌套函数(捕获上下文)最初是在函数式编程语言中引入的(从λ演算中引入),所有(或至少大多数)函数式编程语言都有它们。 在命令式范例中,Pascal家族一直拥有嵌套过程,并且一些较新的C家族也有它们 - 我很确定C#,Java,JavaScript,D,ActionScript等等。 - user180247
1
@Marcus 在Steve314的回答基础上,我使用了“capture”这个词,因为它是C++标准中使用的词语。而且在C++中,它的定义与我所知道的闭包有些不同。特别地,在C++中,捕获可以通过复制或引用来实现,它只影响特定的变量,并且它绝不会影响这些变量的生命周期。 - James Kanze
1
@Steve314,C#没有宏定义,但我使用lambda表达式来替代这个功能——基本上是用于测试静态列表中的项目(即无运行时迭代)的语言缺失的#define宏。 - Blindy
1
@Steve314 C#或Java如何拥有嵌套函数?在这两种语言中,所有的函数都必须是类的成员。 - James Kanze
显示剩余3条评论

2
嵌套函数解释。 点击此处可查看更多关于嵌套函数的信息。

0

嵌套函数只允许在C语言中使用,但很少使用,因为它们仅在该函数范围内可见。然而,如果你想绕过嵌套函数,可以尝试像这样做:

#include<stdio.h>
typedef void (*fptr)(void);
fptr test1(void) {
    void test2(void) {
        printf("test2\n");
    }
printf("test1\n");
return test2;
}
int main(void) {
    void (*f)(void);
    f = test1();
    f();
    return 0;
 }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接