C 怎么没有内嵌函数,但用 C 实现的语言却有呢?

最近我在编写脚本语言 Lua,其中匿名内嵌函数的存在让我纠结了很久。C 语言无论怎样都不能在运行时动态声明函数,那么为什么像 Lua 这样的由 C 实现的语言可以有内嵌函数呢?这是否意味着 C 语言实际上可以实现内嵌函数,只是需要实现一大段代码才能实现呢?

例如

void *block = malloc(sizeof(1) * 1024); // somehow
// write bytes to this memory address to make it operate
// like an inner function?
char (*letterFunct)(int) = ((char (*letterFunct)(int))block;
// somehow trick C into thinking this block is a function?
printf("%c\n", (*letterFunct)(5)); // call it

我错过了什么关键概念,才能理解一些带有高级特性(如类、对象、内嵌函数、多线程)的语言可以在完全没有这些特性的语言中实现么?

点赞
用户1687119
用户1687119

就算某种语言的编译器/解释器是用 C 写的,也不意味着这种语言必须先转化为 C,再进行编译。

我不确定 Lua 是否也是这种情况,但对于 Java 来说,代码是编译成 Java 字节码,然后由 Java 虚拟机读取和解释。

最初的 C 编译器是用汇编语言写的,而最初的 C++ 编译器是用 C 写的,因此在一种低级别语言中编写一种高级别语言的编译器是可行的。

2018-02-19 20:43:05
用户1938552
用户1938552

为了实现内部函数,你需要闭包。为了实现闭包,你需要比堆栈更高级的本地变量分配机制。C语言本意是轻型语言,所以像闭包和垃圾回收这样的高级概念被排除在外。

C++是C的一种扩展,并包含了所有高级概念,包括闭包和内部函数。

关于你使用汇编代码填充的内存块的范例:你可以这样做,但是它并不具备可移植性。它需要操作系统和编译器的协同才能实现。我所能想到的唯一可移植的解决方案是将编译器嵌入到每个可执行文件中,但这也太过复杂了。

而且这仍然是一个普通的非内部函数。要在运行时编译实现内部函数,你还需要闭包。

2018-02-19 20:44:41
用户16406
用户16406

闭包和内部函数通常涉及传递一个额外的(隐藏的)参数给函数,该参数持有闭包的环境。在 C 中,您没有这些隐藏的额外参数,所以要在 C 中实现闭包或内部函数,需要使这些额外参数明确化。因此,要在 C 中实现“内部函数”,您可能需要编写如下代码:

struct shared_locals {
    // 与内部函数共享的函数中的局部变量
};

int inner_function(struct shared_locals *sl,/* 其他传给内部函数的参数 */...) {
    // 内部函数的代码——通过 sl 访问共享局部变量
}

int function(...) {
    struct shared_locals sl;  // 共享的局部变量

    // 直接调用内部函数
    inner_function(&sl, ...);

    // 将内部函数作为回调函数传递
    func_with_callback(inner_function, &sl);
}

上述代码是为什么 C 代码中的“回调”通常涉及使用函数指针和一个额外的 void * 参数传递给回调函数的原因。

2018-02-19 21:21:46
用户801894
用户801894

Lua 是一个程序——就像其他程序一样——你运行它,它读取输入,产生输出。当你运行 lua MyProgram.lua 时,lua 程序从文件 MyProgram.lua 中读取,并将输出写入控制台。和其他许多程序一样,它输出的内容取决于它所读入的内容。

Lua 程序是用 C 写的。

如果你的 MyProgram.lua 文件在顶层包含 print("x"),当 lua 程序读取到那一行时,它将打印 x

注意:是 lua 打印了 x。它并不是_真正的_ MyProgram.luaMyProgram.lua 只是一个数据文件。lua 程序将其读入,并使用数据决定它应该做什么。

lua 程序读入这一行时,它不会将这一行“翻译”成 C 或任何其他语言。它只是按照这一行所说的去做。它打印了 x

这有一个名称:我们说 lua 程序_解释_ MyProgram.lua

注意:我说了谎。lua 程序实际上并没有做任何事情。lua 程序只是一个数据文件。当你输入 lua MyProgram.lua 时,计算机将数据读入内存,然后使用数据来决定它应该做什么。

当我们谈论计算机系统时,我们在不同的_抽象级别_上进行讨论。当我们说:“计算机硬件执行了 X”,我们正在谈论一种_低_抽象级别。当我们说:“MyProgram.lua 执行了 Z”,我们正在谈论一个_更高_的抽象级别。当我们说 lua 解释器执行了某些操作时,我们正在讨论某个处于两者之间的级别。

在硬件和最终用户体验之间,如果你深入思考,你可以找到许多级别的抽象。

但是,回到 Lua…

如果你的 MyProgram.lua 中在顶层包含 function p() print("y") end,那么 Lua 程序不会立即对其进行任何处理。它只是记住你想要 p() 意味着什么。然后稍后,如果它在顶层看到 p(),它就会打印 y

你可以用几乎任何语言编写执行这些操作的程序(例如,你可以编写 Lua)。你选择用什么语言来_实现_ Lua 可能会影响 Lua 解释器的内部架构,但它不会以任何方式限制你的解释器理解的语言(即 Lua 语言)。

2018-02-19 23:00:21
用户500276
用户500276

你把C源代码和二进制可执行文件混淆了。Lua解释器(读取和运行Lua脚本的程序)是用C编写的。但是编译后,它不再是C了。如果它是用Fortran编写的(假设它编译成相同的二进制CPU指令),它的行为也将是相同的。

“运行C环境”这个东西并不存在。只有二进制机器指令。CPU不会像不会说法语一样不会说C语言。

至于Lua如何处理内部函数,Lua的设计者们坐下来,想出了每当解释器遇到内部函数时需要跟踪的所有上下文,并编写了代码来组装并跟踪该上下文,只要内部函数有效。内部函数是一个特定的_Lua_结构——它与C无关,因为当Lua解释器运行时,C不存在。

2018-02-20 04:49:37