📄 lua 5.1 参考手册.txt
字号:
关联在 Lua 函数上的环境用来接管在函数内对全局变量(参见 §2.3)的所有访问。它们也会作为这个函数内创建的其它函数的缺省环境。
你可以通过调用 setfenv 来改变一个 Lua 函数或是正在运行中的线程的环境。而想操控其它对象(userdata、C 函数、其它线程)的环境的话,就必须使用 C API 。
2.10 - 垃圾收集
Lua 提供了一个自动的内存管理。这就是说你不需要关心创建新对象的分配内存操作,也不需要在这些对象不再需要时的主动释放内存。 Lua 通过运行一个垃圾收集器来自动管理内存,以此一遍又一遍的回收死掉的对象(这是指 Lua 中不再访问的到的对象)占用的内存。 Lua 中所有对象都被自动管理,包括: table, userdata、 函数、线程、和字符串。
Lua 实现了一个增量标记清除的收集器。它用两个数字来控制垃圾收集周期: garbage-collector pause 和 garbage-collector step multiplier 。
garbage-collector pause 控制了收集器在开始一个新的收集周期之前要等待多久。随着数字的增大就导致收集器工作工作的不那么主动。小于 1 的值意味着收集器在新的周期开始时不再等待。当值为 2 的时候意味着在总使用内存数量达到原来的两倍时再开启新的周期。
step multiplier 控制了收集器相对内存分配的速度。更大的数字将导致收集器工作的更主动的同时,也使每步收集的尺寸增加。小于 1 的值会使收集器工作的非常慢,可能导致收集器永远都结束不了当前周期。缺省值为 2 ,这意味着收集器将以内存分配器的两倍速运行。
你可以通过在 C 中调用 lua_gc 或是在 Lua 中调用 collectgarbage 来改变这些数字。两者都接受百分比数值(因此传入参数 100 意味着实际值 1 )。通过这些函数,你也可以直接控制收集器(例如,停止或是重启)。
2.10.1 - 垃圾收集的元方法
使用 C API ,你可以给 userdata (参见 §2.8)设置一个垃圾收集的元方法。这个元方法也被称为结束子。结束子允许你用额外的资源管理器和 Lua 的内存管理器协同工作(比如关闭文件、网络连接、或是数据库连接,也可以说释放你自己的内存)。
一个 userdata 可被回收,若它的 metatable 中有 __gc 这个域 ,垃圾收集器就不立即收回它。取而代之的是,Lua 把它们放到一个列表中。最收集结束后,Lua 针对列表中的每个 userdata 执行了下面这个函数的等价操作:
function gc_event (udata)
local h = metatable(udata).__gc
if h then
h(udata)
end
end
在每个垃圾收集周期的结尾,每个在当前周期被收集起来的 userdata 的结束子会以它们构造时的逆序依次调用。也就是说,收集列表中,最后一个在程序中被创建的 userdata 的结束子会被第一个调用。
2.10.2 - Weak Table(弱表)
weak table 是一个这样的 table,它其中的元素都被弱引用。弱引用将被垃圾收集器忽略掉,换句话说,如果对一个对象的引用只有弱引用,垃圾收集器将回收这个对象。
weak table 的键和值都可以是 weak 的。如果一个 table 只有键是 weak 的,那么将运行收集器回收它们的键,但是会阻止回收器回收对应的值。而一个 table 的键和值都是 weak 时,就即允许收集器回收键又允许收回值。任何情况下,如果键和值中任一个被回收了,整个键值对就会从 table 中拿掉。 table 的 weak 特性可以通过在它的 metatable 中设置 __mode 域来改变。如果 __mode 域中是一个包含有字符 'k' 的字符串时, table 的键就是 weak 的。如果 __mode 域中是一个包含有字符 'v' 的字符串时, table 的值就是 weak 的。
在你把一个 table 当作一个 metatable 使用之后,就不能再修改 __mode 域的值。否则,受这个 metatable 控制的 table 的 weak 行为就成了未定义的。
2.11 - Coroutine (协同例程)
Lua 支持 coroutine ,这个东西也被称为协同式多线程 (collaborative multithreading) 。 Lua 为每个 coroutine 提供一个独立的运行线路。然而和多线程系统中的线程不同,coroutine 只在显式的调用了 yield 函数时才会挂起。
创建一个 coroutine 需要调用一次 coroutine.create 。它只接收单个参数,这个参数是 coroutine 的主函数。 create 函数仅仅创建一个新的 coroutine 然后返回它的控制器(一个类型为 thread 的对象);它并不会启动 coroutine 的运行。
当你第一次调用 coroutine.resume 时,所需传入的第一个参数就是 coroutine.create 的返回值。这时,coroutine 从主函数的第一行开始运行。接下来传入 coroutine.resume 的参数将被传进 coroutine 的主函数。在 coroutine 开始运行后,它讲运行到自身终止或是遇到一个 yields 。
coroutine 可以通过两种方式来终止运行:一种是正常退出,指它的主函数返回(最后一条指令被运行后,无论有没有显式的返回指令); 另一种是非正常退出,它发生在未保护的错误发生的时候。第一种情况中, coroutine.resume 返回 true ,接下来会跟着 coroutine 主函数的一系列返回值。第二种发生错误的情况下, coroutine.resume 返回 false ,紧接着是一条错误信息。
coroutine 中切换出去,可以调用 coroutine.yield。当 coroutine 切出,与之配合的 coroutine.resume 就立即返回,甚至在 yield 发生在内层的函数调用中也可以(就是说,这不限于发生在主函数中,也可以是主函数直接或间接调用的某个函数里)。在 yield 的情况下,coroutine.resume 也是返回 true,紧跟着那些被传入 coroutine.yield 的参数。等到下次你在继续同样的 coroutine ,将从调用 yield 的断点处运行下去。断点处 yield 的返回值将是 coroutine.resume 传入的参数。
类似 coroutine.create , coroutine.wrap 这个函数也将创建一个 coroutine ,但是它并不返回 coroutine 本身,而是返回一个函数取而代之。一旦你调用这个返回函数,就会切入 coroutine 运行。所有传入这个函数的参数等同于传入 coroutine.resume 的参数。 coroutine.wrap 会返回所有应该由除第一个(错误代码的那个布尔量)之外的由 coroutine.resume 返回的值。和 coroutine.resume 不同, coroutine.wrap 不捕获任何错误;所有的错误都应该由调用者自己传递。
看下面这段代码展示的一个例子:
function foo (a)
print("foo", a)
return coroutine.yield(2*a)
end
co = coroutine.create(function (a,b)
print("co-body", a, b)
local r = foo(a+1)
print("co-body", r)
local r, s = coroutine.yield(a+b, a-b)
print("co-body", r, s)
return b, "end"
end)
print("main", coroutine.resume(co, 1, 10))
print("main", coroutine.resume(co, "r"))
print("main", coroutine.resume(co, "x", "y"))
print("main", coroutine.resume(co, "x", "y"))
当你运行它,将得到如下输出结果:
co-body 1 10
foo 2
main true 4
co-body r
main true 11 -9
co-body x y
main true 10 end
main false cannot resume dead coroutine
3 - 程序接口(API)
这个部分描述了 Lua 的 C API ,也就是宿主程序跟 Lua 通讯用的一组 C 函数。所有的 API 函数按相关的类型以及常量都声明在头文件 lua.h 中。
虽然我们说的是“函数”,但一部分简单的 API 是以宏的形式提供的。所有的这些宏都只使用它们的参数一次(除了第一个参数,也就是 lua 状态机),因此你不需担心这些宏的展开会引起一些副作用。
在所有的 C 库中,Lua API 函数都不去检查参数的有效性和坚固性。然而,你可以在编译 Lua 时加上打开一个宏开关来开启 luaconf.h 文件中的宏 luai_apicheck 以改变这个行为。
3.1 - 堆栈
Lua 使用一个虚拟栈来和 C 传递值。栈上的的每个元素都是一个 Lua 值(nil,数字,字符串,等等)。
无论何时 Lua 调用 C,被调用的函数都得到一个新的栈,这个栈独立于 C 函数本身的堆栈,也独立于以前的栈。(译注:在 C 函数里,用 Lua API 不能访问到 Lua 状态机中本次调用之外的堆栈中的数据)它里面包含了 Lua 传递给 C 函数的所有参数,而 C 函数则把要返回的结果也放入堆栈以返回给调用者(参见 lua_CFunction)。
方便起见,所有针对栈的 API 查询操作都不严格遵循栈的操作规则。而是可以用一个索引来指向栈上的任何元素:正的索引指的是栈上的绝对位置(从一开始);负的索引则指从栈顶开始的偏移量。更详细的说明一下,如果堆栈有 n 个元素,那么索引 1 表示第一个元素(也就是最先被压入堆栈的元素)而索引 n 则指最后一个元素;索引 -1 也是指最后一个元素(即栈顶的元素),索引 -n 是指第一个元素。如果索引在 1 到栈顶之间(也就是,1 ≤ abs(index) ≤ top)我们就说这是个有效的索引。
3.2 - 堆栈尺寸
当你使用 Lua API 时,就有责任保证其坚固性。特别需要注意的是,你有责任控制不要堆栈溢出。你可以使用 lua_checkstack 这个函数来扩大可用堆栈的尺寸。
无论何时 Lua 调用 C ,它都只保证 LUA_MINSTACK 这么多的堆栈空间可以使用。 LUA_MINSTACK 一般被定义为 20 ,因此,只要你不是不断的把数据压栈,通常你不用关心堆栈大小。
所有的查询函数都可以接收一个索引,只要这个索引是任何栈提供的空间中的值。栈能提供的最大空间是通过 lua_checkstack 来设置的。这些索引被称作可接受的索引,通常我们把它定义为:
(index < 0 && abs(index) <= top) ||
(index > 0 && index <= stackspace)
注意,0 永远都不是一个可接受的索引。(译注:下文中凡提到的索引,没有特别注明的话,都指可接受的索引。)
3.3 - 伪索引
除了特别声明外,任何一个函数都可以接受另一种有效的索引,它们被称作“伪索引”。这个可以帮助 C 代码访问一些并不在栈上的 Lua 值。伪索引被用来访问线程的环境,函数的环境,注册表,还有 C 函数的 upvalue (参见 §3.4)。
线程的环境(也就是全局变量放的地方)通常在伪索引 LUA_GLOBALSINDEX 处。正在运行的 C 函数的环境则放在伪索引 LUA_ENVIRONINDEX 之处。
你可以用常规的 table 操作来访问和改变全局变量的值,只需要指定环境表的位置。举例而言,要访问全局变量的值,这样做:
lua_getfield(L, LUA_GLOBALSINDEX, varname);
3.4 - C Closure
当 C 函数被创建出来,我们有可能会把一些值关联在一起,也就是创建一个 C closure ;这些被关联起来的值被叫做 upvalue ,它们可以在函数被调用的时候访问的到。(参见 lua_pushcclosure)。
无论何时去调用 C 函数,函数的 upvalue 都被放在指定的伪索引处。我们可以用 lua_upvalueindex 这个宏来生成这些伪索引。第一个关联到函数的值放在 lua_upvalueindex(1) 位置处,依次类推。任何情况下都可以用 lua_upvalueindex(n) 产生一个 upvalue 的索引,即使 n 大于实际的 upvalue 数量也可以。它都可以产生一个可接受但不一定有效的索引。
3.5 - 注册表
Lua 提供了一个注册表,这是一个预定义出来的表,可以用来保存任何 C 代码想保存的 Lua 值。这个表可以用伪索引 LUA_REGISTRYINDEX 来定位。任何 C 库都可以在这张表里保存数据,为了防止冲突,你需要特别小心的选择键名。一般的用法是,你可以用一个包含你的库名的字符串做为键名,或者可以取你自己 C 代码中的一个地址,以 light userdata 的形式做键。
注册表里的整数健被用于补充库中实现的引用系统的工作,一般说来不要把它们用于别的用途。
3.6 - C 中的错误处理
在内部实现中,Lua 使用了 C 的 longjmp 机制来处理错误。(如果你使用 C++ 的话,也可以选择换用异常;参见 luaconf.h 文件。)当 Lua 碰到任何错误(比如内存分配错误、类型错误、语法错误、还有一些运行时错误)它都会产生一个错误出去;也就是调用一个 long jump 。在保护环境下,Lua 使用 setjmp 来设置一个恢复点;任何发生的错误都会激活最近的一个恢复点。
几乎所有的 API 函数都可能产生错误,例如内存分配错误。但下面的一些函数运行在保护环境中(也就是说它们创建了一个保护环境再在其中运行),因此它们不会产生错误出来: lua_newstate, lua_close, lua_load, lua_pcall, and lua_cpcall。
在 C 函数里,你也可以通过调用 lua_error 产生一个错误。
3.7 - 函数和类型
在这里我们按字母次序列出了所有 C API 中的函数和类型。
--------------------------------------------------------------------------------
lua_Alloc
typedef void * (*lua_Alloc) (void *ud,
void *ptr,
size_t osize,
size_t nsize);
Lua 状态机中使用的内存分配器函数的类型。内存分配函数必须提供一个功能类似于 realloc 但又不完全相同的函数。它的参数有 ud ,一个由 lua_newstate 传给它的指针; ptr ,一个指向已分配出来或是将被重新分配或是要释放的内存块指针; osize ,内存块原来的尺寸; nsize ,新内存块的尺寸。如果且只有 osize 是零时,ptr 为 NULL 。当 nsize 是零,分配器必须返回 NULL;如果 osize 不是零,分配器应当释放掉 ptr 指向的内存块。当 nsize 不是零,若分配器不能满足请求时,分配器返回 NULL 。当 nsize 不是零而 osize 是零时,分配器应该和 malloc 有相同的行为。当 nsize 和 osize 都不是零时,分配器则应和 realloc 保持一样的行为。 Lua 假设分配器在 osize >= nsize 时永远不会失败。
这里有一个简单的分配器函数的实现。这个实现被放在补充库中,由 luaL_newstate 提供。
static void *l_alloc (void *ud, void *ptr, size_t osize,
size_t nsize) {
(void)ud; (void)osize; /* not used */
if (nsize == 0) {
free(ptr);
return NULL;
}
else
return realloc(ptr, nsize);
}
这段代码假设 free(NULL) 啥也不影响,而且 realloc(NULL, size) 等价于 malloc(size)。这两点是 ANSI C 保证的行为。
--------------------------------------------------------------------------------
lua_atpanic
lua_CFunction lua_atpanic (lua_State *L, lua_CFunction panicf);
设置一个新的 panic (恐慌) 函数,并返回前一个。
如果在保护环境之外发生了任何错误, Lua 就会调用一个 panic 函数,接着调用 exit(EXIT_FAILURE),这样就开始退出宿主程序。你的 panic 函数可以永远不返回(例如作一次长跳转)来避免程序退出。
panic 函数可以从栈顶取到出错信息。
--------------------------------------------------------------------------------
lua_call
void lua_call (lua_State *L, int nargs, int nresults);
调用一个函数。
要调用一个函数请遵循以下协议:首先,要调用的函数应该被压入堆栈;接着,把需要传递给这个函数的参数按正序压栈;这是指第一个参数首先压栈。最后调用一下 lua_call; nargs 是你压入堆栈的参数个数。当函数调用完毕后,所有的参数以及函数本身都会出栈。而函数的返回值这时则被压入堆栈。返回值的个数将被调整为 nresults 个,除非 nresults 被设置成 LUA_MULTRET。在这种情况下,所有的返回值都被压入堆栈中。 Lua 会保证返回值都放入栈空间中。函数返回值将按正序压栈(第一个返回值首先压栈),因此在调用结束后,最后一个返回值将被放在栈顶。
被调用函数内发生的错误将(通过 longjmp)一直上抛。
下面的例子中,这行 Lua 代码等价于在宿主程序用 C 代码做一些工作:
a = f("how", t.x, 14)
这里是 C 里的代码:
lua_getfield(L, LUA_GLOBALSINDEX, "f"); /* 将调用的函数 */
lua_pushstring(L, "how"); /* 第一个参数 */
lua_getfield(L, LUA_GLOBALSINDEX, "t"); /* table 的索引 */
lua_getfield(L, -1, "x"); /* 压入 t.x 的值(第 2 个参数)*/
lua_remove(L, -2); /* 从堆栈中移去 't' */
lua_pushinteger(L, 14); /* 第 3 个参数 */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -