为什么C标准不支持嵌套函数?

在assembly中实现它似乎不太难。

gcc还有一个标志(-fnested-functions)来启用它们。

事实certificate,它们实际上并不是那么容易实现的。

内部函数是否可以访问包含范围的变量? 如果没有,嵌套就没有意义了; 只是让它静态(以限制它所在的翻译单元的可见性)并添加一条评论说“这是一个仅由myfunc()使用的辅助函数”。

但是,如果要访问包含范围的变量,则基本上强制它生成闭包(替代方法是限制使用嵌套函数可以做的事情,使它们变得无用)。 我认为GCC实际上通过为包含函数的每次调用生成(在运行时)一个唯一的thunk来处理这个问题,它设置一个上下文指针然后调用嵌套函数。 这最终是一个相当Icky hack,以及一些完全合理的实现无法做到的事情(例如,在禁止执行可写内存的系统上 – 许多现代操作系统出于安全原因这样做)。 使其一般工作的唯一合理方法是强制所有函数指针携带隐藏的上下文参数,并且所有函数都接受它(因为在一般情况下,您不知道何时调用它是否是闭包或一个未公开的function)。 出于技术和文化方面的原因,这在C语言中是不合适的,因此我们坚持使用显式上下文指针来伪造闭包而不是嵌套函数,或者使用具有所需基础结构的更高级语言。做得好。

我想引用 BDFL(Guido van Rossum)的话:

这是因为嵌套函数定义无法访问周围块的局部变量 – 只能访问包含模块的全局变量。 这样做是为了查找全局变量不必走一串字典 – 就像在C中一样,只有两个嵌套的范围:locals和globals(除此之外,内置函数)。 因此,嵌套函数的用途有限。 这是一个深思熟虑的决定,基于允许任意嵌套的语言(如Pascal和Algols)的经验 – 具有太多嵌套作用域的代码与具有太多GOTO的代码一样可读。

重点是我的。

我相信他指的是Python中的嵌套作用域(正如David在评论中指出的那样,这是从1993年开始,Python现在支持完全嵌套的函数) – 但我认为该语句仍然适用。

它的另一部分可能是关闭 。

如果你有像这样的C代码的函数:

(*int()) foo() { int x = 5; int bar() { x = x + 1; return x; } return &bar; } 

如果你在某种回调中使用bar ,x会发生什么? 这在许多较新的高级语言中都有明确定义,但是AFAIK没有明确定义的方法来跟踪C中的x – 每次都会返回6,还是连续调用bar返回递增值? 这可能会为C的相对简单的定义增加一个全新的复杂层。

有关潜在问题,请参阅C FAQ 20.24和GCC手册 :

如果你试图在包含函数退出后通过其地址调用嵌套函数,那么所有的地狱都会破裂。 如果你试图在包含范围级别退出后调用它,并且如果它引用了一些不再在范围内的变量,那么你可能会很幸运,但冒风险并不明智。 但是,如果嵌套函数没有引用超出范围的任何内容,那么您应该是安全的。

这并不比C标准的其他一些有问题的部分更严重,所以我说原因主要是历史性的(C99与K&R C特征并不完全不同)。

在某些情况下,具有词法范围的嵌套函数可能会有用(考虑一个递归的内部函数,它不需要额外的堆栈空间用于外部作用域中的变量而不需要静态变量),但希望您可以信任编译器正确地内联这些函数,即具有单独函数的解决方案将更加冗长。

嵌套函数是一个非常微妙的东西。 你会把它们封闭吗? 如果没有,那么它们对常规函数没有好处,因为它们不能访问任何局部变量。 如果他们这样做,那么你如何处理堆栈分配的变量呢? 你必须将它们放在其他地方,以便以后调用嵌套函数时,变量仍然存在。 这意味着它们将占用内存,因此您必须在堆上为它们分配空间。 没有GC,这意味着程序员现在负责清理function。 等等… C#做到这一点,但他们有一个GC,它是比C更新的语言。

将成员函数添加到结构体也不会太难,但它们也不在标准中。

无论是否易于实现,function都不会添加到基于soley的C标准中。 它是许多其他因素的组合,包括标准编写的时间点和当时/实际的标准。

ANSI C已成立20年。 也许在1983年到1989年之间,委员会可能会根据当时的编译技术状况对其进行讨论,但如果他们这样做,他们的推理就会在昏暗和遥远的过去中消失。

我不同意Dave Vandervies。

定义嵌套函数比在全局范围内定义嵌套函数要好得多,使其成为静态并添加注释“这是一个仅由myfunc()使用的辅助函数”。

如果您需要辅助函数来帮助该函数怎么办? 你会添加注释“这是仅由myfunc使用的第一个辅助函数的辅助函数”吗? 在没有完全污染命名空间的情况下,您从哪里获取所有这些函数所需的名称?

编写代码有多困惑?

但是,当然,存在如何处理闭包的问题,​​即返回指向函数的指针,该函数可以访问返回它的函数中定义的变量。

还有一个原因:嵌套函数很有价值并不清楚。 二十多年前,我曾经在(VAX)Pascal进行大规模的编程和维护。 我们有很多旧代码,大量使用嵌套函数。 起初,我认为这很酷(与之前一直在工作的K&R C相比)并开始自己做。 过了一会儿,我觉得这是一场灾难,然后停了下来。

问题是函数可能在范围内有很多变量,计算它嵌套的所有函数的变量。 (有些旧代码有十级嵌套;五代很常见,直到我改变主意,我自己编写了一些后者。)嵌套堆栈中的变量可以有相同的名称,这样“内部”函数局部变量可以在更多“外部”函数中屏蔽同名变量。 函数的局部变量,在C语言中完全私有,可以通过调用嵌套函数来修改。 这种爵士乐的可能组合几乎是无限的,这是读取代码时理解的噩梦。

所以,我开始调用这个编程构造“半全局变量”而不是“嵌套函数”,并告诉其他人在编写代码时唯一比全局变量更糟的是半全局变量,请不要创建再也没有。 如果可以的话,我会用语言禁止它。 可悲的是,编译器没有这样的选择……

您不允许在包含的函数中引用包含函数的局部变量,并且嵌套只是一个没有太多用处的作用域function,或者您这样做。 如果这样做,它不是一个如此简单的function:您必须能够在访问正确数据时从另一个调用嵌套函数,并且还必须考虑递归调用。 这并非不可能 – 技术众所周知,并且在设计C时已经掌握得很好(Algol 60已经具有该function)。 但它使运行时组织和编译器变得复杂,并且阻止了对汇编语言的简单映射(函数指针必须包含有关它的信息;还有一些替代方法,例如使用gcc)。 它超出了系统实现语言C的设计范围。

你的问题和它的答案形成了重言式……为什么? 因为!

我不妨问为什么C没有,哦我不知道,通过引用传递而不是默认传递值。 或者其他任何没有的东西。

如果你想要嵌套函数,那就好了! 查找并使用支持它们的语言。