在 Lua 的 C API 中,有一个特殊的表,叫做 注册表(Registry)。它不是 Lua 脚本中能直接访问的普通表,而是专为 C 代码设计的全局存储区。
一、注册表是什么?
- 注册表是一个普通的 Lua 表,但只有 C 代码能访问。
- 它的“地址”是固定的:使用伪索引
LUA_REGISTRYINDEX
(值为 -10000)。 - Lua 脚本中没有变量指向它,因此脚本无法直接读写它。
- 它的生命周期和 Lua 状态机(
lua_State* L
)一致 —— 只要 L 存在,注册表就存在。 - 所以,注册表不是
_G
(全局表)
二、为什么需要注册表?
当你在 C 中调用 Lua,有时需要:
- 保存一个 Lua 函数,以后回调它(比如事件处理函数)
- 保存一个 Lua 表或 userdata,避免被垃圾回收
- 在多个 C 函数之间传递 Lua 对象
直接保存 lua_State*
和栈位置是不安全的 —— 栈会变,对象可能被 GC。
注册表提供了一个安全、稳定、长期存储 Lua 值的地方。
三、怎么使用注册表?
最常用、最安全的方式是配合 luaL_ref
和 luaL_unref
:
1. 保存值 → 获取引用编号
lua_pushvalue(L, idx); // 把你想保存的值推到栈顶
int ref = luaL_ref(L, LUA_REGISTRYINDEX); // 存入注册表,返回整数引用
2. 取回值
lua_rawgeti(L, LUA_REGISTRYINDEX, ref); // 把值重新压入栈顶
3. 释放值(重要!)
luaL_unref(L, LUA_REGISTRYINDEX, ref); // 释放引用,避免内存泄漏
四、注意事项
- ✅ 推荐只用
luaL_ref
返回的整数作为键 —— 这是安全区,不会和 Lua 内部冲突。 - ❌ 避免用字符串键(如
"mydata"
)—— Lua 内部可能已占用某些键(如"IO"
),有冲突风险。 - 🧹 用完一定要
luaL_unref
—— 否则对象会一直留在注册表中,造成内存泄漏。
五、典型应用场景
- 保存 Lua 回调函数供 C 触发
- 在 C 结构体中存储 Lua 对象的“句柄”
- 实现 C 与 Lua 之间的对象生命周期管理
- 跨多个 C 函数共享 Lua 数据
六、总结
注册表是 Lua C API 中一个简单但非常实用的机制:
它让 C 代码能安全地“记住”Lua 中的对象,不怕栈变化,不怕 GC 回收。
使用方法也很简单:
luaL_ref → 保存,拿编号
lua_rawgeti → 取回
luaL_unref → 释放
发表回复