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
我错过了什么关键概念,才能理解一些带有高级特性(如类、对象、内嵌函数、多线程)的语言可以在完全没有这些特性的语言中实现么?
为了实现内部函数,你需要闭包。为了实现闭包,你需要比堆栈更高级的本地变量分配机制。C语言本意是轻型语言,所以像闭包和垃圾回收这样的高级概念被排除在外。
C++是C的一种扩展,并包含了所有高级概念,包括闭包和内部函数。
关于你使用汇编代码填充的内存块的范例:你可以这样做,但是它并不具备可移植性。它需要操作系统和编译器的协同才能实现。我所能想到的唯一可移植的解决方案是将编译器嵌入到每个可执行文件中,但这也太过复杂了。
而且这仍然是一个普通的非内部函数。要在运行时编译实现内部函数,你还需要闭包。
闭包和内部函数通常涉及传递一个额外的(隐藏的)参数给函数,该参数持有闭包的环境。在 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 * 参数传递给回调函数的原因。
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 解释器执行了某些操作时,我们正在讨论某个处于两者之间的级别。
在硬件和最终用户体验之间,如果你深入思考,你可以找到许多级别的抽象。
但是,回到 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不存在。
- Lua 虚拟机加密load(string.dump(function)) 后执行失败问题如何解决
- 我想创建一个 Nginx 规则,禁止访问
- 如何将两个不同的lua文件合成一个 东西有点长 大佬请耐心看完 我是小白研究几天了都没搞定
- 如何在roblox studio中1:1导入真实世界的地形?
- 求解,lua_resume的第二次调用继续执行协程问题。
- 【上海普陀区】内向猫网络招募【Skynet游戏框架Lua后端程序员】
- SF爱好求教:如何用lua实现游戏内调用数据库函数实现账号密码注册?
- Lua实现网站后台开发
- LUA错误显式返回,社区常见的规约是怎么样的
- lua5.3下载库失败
- 请问如何实现文本框内容和某个网页搜索框内容连接,并把网页输出来的结果反馈到另外一个文本框上
- lua lanes多线程使用
- 一个kv数据库
- openresty 有没有比较轻量的 docker 镜像
- 想问一下,有大佬用过luacurl吗
- 在Lua执行过程中使用Load函数出现问题
- 为什么 neovim 里没有显示一些特殊字符?
- Lua比较两个表的值(不考虑键的顺序)
- 有个lua简单的项目,外包,有意者加微信 liuheng600456详谈,最好在成都
- 如何在 Visual Studio 2022 中运行 Lua 代码?
