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?
// is that even possible?
char (*letterFunct)(int) = ((char (*letterFunct)(int))block;
// somehow trick C into thinking this block is a function?
printf("%cn", (*letterFunct)(5)); // call it

我缺少的关键概念是什么,可以弥合理解为什么某些具有高级功能(类,对象,内部函数,多线程)的语言可以在没有所有这些功能的语言中实现的差距?

仅仅因为特定语言的编译器/解释器是用 C 编写的并不意味着该语言必须翻译成 C 然后被编译。

我不了解Lua,但在Java的情况下,代码被编译为Java字节码,(松散地说)Java VM读取和解释。

原始 C 编译器是用汇编语言编写的,而原始C++编译器是用 C 编写的,因此可以使用较低级别的语言为高级语言编写编译器。

闭包和内部函数通常涉及将一个额外的(隐藏)参数传递给保存闭包环境的函数。 在 C 中,您没有那些隐藏的额外参数,因此要在 C 中实现闭包或内部函数,您需要将这些额外的参数显式化。 因此,要在 C 语言中实现"内部函数",您最终可能会得到如下内容:

struct shared_locals {
// locals of function shared with inner function
};
int inner_function(struct shared_locals *sl, /* other args to inner function */...) {
// code for the inner function -- shared locals accessed va sl
}
int function(...) {
struct shared_locals sl;  // the shared locals
// call inner function directly
inner_function(&sl, ...);
// pass inner function as a callback
func_with_callback(inner_function, &sl);
}

上面的代码是为什么 C 代码中的"回调"通常涉及函数指针和传递给回调的额外void *参数。

要实现内部函数,您需要闭包。要实现闭包,您需要一些更高级的分配局部变量的机制,而不仅仅是堆栈。C 是一种轻量级语言,因此排除了闭包和垃圾收集器等高级概念。

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

使用汇编程序代码填充的内存块的示例:您可以这样做,但它不是可移植的。这需要操作系统和编译器的合作。我能想到的唯一可移植解决方案是将编译器嵌入到每个可执行文件中,这又太多了。

这仍然是一个正常的非内在功能。要实现在运行时编译的内部函数,您将再次需要闭包。

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

lua 程序是用 C 语言编写的。

如果您的MyProgram.lua文件包含顶层print("x"),则当lua程序读取该行时,它将打印x

注:lua打印x。 这不是真的MyProgram.lua.MyProgram.lua只是一个数据文件。lua程序读取它,并使用数据来决定它应该做什么。

lua程序读取该行时,它不会将该行"翻译"为C或任何其他语言。 它只是按照行上说的去做。 它打印x.

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

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

当我们谈论计算机系统时,我们谈论的是不同的抽象层次。 当我们说"计算机硬件做了X"时,我们说的是一个层次的抽象。 当我们说"MyProgram.lua做了Z"时,我们谈论的是更高层次的抽象。 而且,当我们说lua口译员做了某事时,我们谈论的是介于两者之间的某个级别。

在硬件和最终用户体验之间,如果您看得足够深入,可以找到许多抽象级别。

但是,回到路亚...

如果您的MyProgram.lua包含顶级function p() print("y") end,那么Lua程序不会立即对此执行任何操作。 它只是记住你想p()的意思。 然后,如果它在顶层看到p()打印y

你可以用几乎任何语言编写做这些事情的程序(即,你可以写Lua)。 你选择用于实现Lua的语言可能会影响Lua解释器的内部架构,但它不需要以任何方式限制解释器理解的语言(即Lua语言)。

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

没有所谓的"运行 C 环境"。只有二进制机器指令。CPU对C的了解程度不比法语多。

至于Lua如何处理内部函数,Lua的设计者坐下来,找出了每当解释器遇到内部函数时他们需要跟踪的所有上下文,并编写代码来组装和跟踪该上下文,只要内部函数可行。内部函数是一个特定的Lua构造 - 它与 C 无关,因为当 Lua 解释器运行时,任何地方都没有 C。

最新更新