状态机与内存管理
除了 luaL_newstate() 这种开箱即用的方式,Lua C API 还提供了更底层的状态机控制接口。
自定义内存分配器
luaL_newstate() 内部使用标准 malloc/free。如果你需要自定义内存管理(如使用内存池、追踪泄漏、限制总量),可以使用
lua_newstate:
lua_State *lua_newstate(lua_Alloc f, void *ud);
lua_Alloc 的签名:
typedef void * (*lua_Alloc)(void *ud, void *ptr, size_t osize, size_t nsize);
| 场景 | 参数值 | 行为 |
|---|---|---|
| 分配新块 | ptr=NULL, osize=0, nsize>0 | 分配 nsize 字节 |
| 重新分配 | ptr!=NULL, osize=原大小, nsize>0 | 调整为 nsize 字节 |
| 释放 | ptr!=NULL, nsize=0 | 释放 ptr |
osize 的值还暗示了分配用途(Lua 内部用此做统计):
osize == LUA_TSTRING等:为特定类型分配- 其他值:原始字节数
Extra Space
每个 lua_State 在头部预留了一块额外空间(大小由 LUAI_EXTRASPACE 决定),可通过宏访问:
void *extra = lua_getextraspace(L);
常用于存储与状态机关联的自定义上下文指针,避免全局变量。
查询与替换分配器
lua_Alloc old = lua_getallocf(L, &old_ud);
lua_setallocf(L, my_alloc, my_ud);
栈转移
当有两个 lua_State*(如主线程和协程)时,可以用 lua_xmove 在它们之间转移栈值:
lua_xmove(from, to, n); // 从 'from' 栈顶弹出 n 个值,压入 'to'
lua_xmove 要求两个状态机共享同一个全局表(即由 lua_newthread 创建的协程)。任意两个独立状态机之间不能使用
lua_xmove。
版本查询
lua_Number ver = lua_version(L);
返回 LUA_VERSION_NUM(如 504),可用于运行时版本检查。
Panic 函数
当 Lua 错误发生在没有任何保护帧的情况下(极其严重的编程错误),Lua 会调用 panic 函数:
lua_CFunction old = lua_atpanic(L, my_panic);
默认 panic 函数打印错误信息并调用 abort()。嵌入到游戏引擎或 GUI 程序时,通常需要替换为自定义实现以优雅退出。
完整代码
#include <assert.h>
#include <lauxlib.h>
#include <lua.h>
#include <lualib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static size_t total_allocated = 0;
static size_t total_freed = 0;
static size_t current_used = 0;
static void *my_alloc(void *ud, void *ptr, size_t osize, size_t nsize) {
(void)ud;
(void)osize;
if (nsize == 0) {
if (ptr) {
total_freed += osize;
current_used -= osize;
free(ptr);
}
return NULL;
} else {
void *newp = realloc(ptr, nsize);
if (newp && ptr == NULL) {
total_allocated += nsize;
current_used += nsize;
} else if (newp && ptr != NULL) {
current_used = current_used - osize + nsize;
total_allocated += (nsize > osize) ? (nsize - osize) : 0;
}
return newp;
}
}
static int my_panic(lua_State *L) {
const char *msg = lua_tostring(L, -1);
fprintf(stderr, "PANIC: unprotected error in call to Lua API (%s)\n",
msg ? msg : "?");
return 0;
}
int main(void) {
lua_State *L = lua_newstate(my_alloc, NULL);
if (!L) {
fputs("cannot create state\n", stderr);
return 1;
}
lua_atpanic(L, my_panic);
assert(lua_version(L) == 504);
luaL_openlibs(L);
{
int *ctx = (int *)lua_getextraspace(L);
*ctx = 42;
assert(*(int *)lua_getextraspace(L) == 42);
}
size_t before = current_used;
(void)luaL_dostring(L, "local t = {} for i = 1, 1000 do t[i] = i end");
assert(current_used > before);
lua_State *co = lua_newthread(L);
lua_pushstring(L, "hello from main");
lua_xmove(L, co, 1);
assert(strcmp(lua_tostring(co, -1), "hello from main") == 0);
lua_pop(co, 1);
void *ud;
lua_Alloc allocf = lua_getallocf(L, &ud);
assert(allocf == my_alloc);
assert(ud == NULL);
lua_close(L);
assert(total_allocated > 0);
assert(total_freed > 0);
puts("12-state: ok");
return 0;
}