第一版是为 Lua 5.0 编写的。虽然在很大程度上仍然适用于后续版本,但仍有一些差异。
第四版针对 Lua 5.3,可在 Amazon 和其他书店购买。
购买本书,您还可以帮助 支持 Lua 项目


24.2 – 栈

在尝试在 Lua 和 C 之间交换值时,我们面临两个问题:动态类型系统和静态类型系统之间的不匹配,以及自动内存管理和手动内存管理之间的不匹配。

在 Lua 中,当我们编写 a[k] = v 时,kv 都可以具有多种不同的类型(甚至 a 也可能具有不同的类型,因为元表)。但是,如果我们希望在 C 中提供此操作,则任何 settable 函数都必须具有固定类型。对于此单一操作,我们需要几十个不同的函数(对于三个参数的每种类型组合,一个函数)。

我们可以通过在 C 中声明某种联合类型来解决此问题,我们称之为 lua_Value,它可以表示所有 Lua 值。然后,我们可以将 settable 声明为

    void lua_settable (lua_Value a, lua_Value k, lua_Value v);
此解决方案有两个缺点。首先,将如此复杂的类型映射到其他语言可能很困难;Lua 的设计不仅可以轻松地与 C/C++ 接口,还可以与 Java、Fortran 等接口。其次,Lua 会执行垃圾回收:如果我们在 C 变量中保留 Lua 值,则 Lua 引擎无法知道此用法;它可能会(错误地)假设此值为垃圾并收集它。

因此,Lua API 不会定义类似 lua_Value 类型的内容。相反,它使用一个抽象堆栈在 Lua 和 C 之间交换值。此堆栈中的每个槽位都可以容纳任何 Lua 值。每当您想从 Lua 请求一个值(例如全局变量的值)时,您调用 Lua,它将所需值压入堆栈。每当您想将一个值传递给 Lua 时,您首先将值压入堆栈,然后调用 Lua(它将弹出该值)。我们仍然需要不同的函数来将每个 C 类型压入堆栈,以及不同的函数来从堆栈获取每个值,但我们避免了组合爆炸。此外,由于此堆栈由 Lua 管理,因此垃圾回收器知道 C 正在使用哪些值。

API 中几乎所有函数都使用堆栈。正如我们在第一个示例中看到的,luaL_loadbuffer 将其结果留在堆栈中(编译的块或错误消息);lua_pcall 从堆栈中获取要调用的函数,并将任何可能的错误消息留在那里。

Lua 以严格的 LIFO 规则(后进先出;即始终通过顶部)操纵此堆栈。当您调用 Lua 时,它只会更改堆栈的顶部。您的 C 代码有更大的自由度;具体来说,它可以检查堆栈中的任何元素,甚至可以在任意位置插入和删除元素。