Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Userdata

Userdata 是 Lua 用来在脚本中保存任意 C 数据的类型。它是 C 扩展 Lua 的基石。

两种 Userdata

类型API特点
Full userdatalua_newuserdatauv(L, size, nuvalue)Lua 分配内存,受 GC 管理
Light userdatalua_pushlightuserdata(L, p)仅保存 C 指针,不受 GC 管理

Full Userdata 详解

创建

// 分配一块大小为 sizeof(MyStruct) 的内存
MyStruct *obj = (MyStruct *)lua_newuserdatauv(L, sizeof(MyStruct), 0);
obj->x = 0;
obj->y = 0;

lua_newuserdatauv 的第三个参数是关联的 uservalue 数量(Lua 5.4), 用于将 Lua 值与 userdata 关联。此处设为 0 表示不关联。

创建后,userdata 在 Lua 中是一个完整对象,可以赋给变量、放入 table、 作为参数传递。当不再被引用时,Lua GC 会自动回收它。

读写

C 代码通过返回的指针直接读写内存:

// 从 Lua 获取 userdata
MyStruct *obj = (MyStruct *)lua_touserdata(L, 1);
obj->x += 10;

Lua 代码只能持有 userdata 引用,不能直接访问其字段。 通常通过 C 函数来读写(第 8 章会讲 metatable 如何让 userdata 支持 . 访问)。

Light Userdata

用于传递已存在的 C 指针:

void *some_c_ptr = ...;
lua_pushlightuserdata(L, some_c_ptr);
lua_setglobal(L, "c_handle");

注意:Light userdata 不受 GC 管理,生命周期由 C 代码保证。 它也没有独立的元表(所有 light userdata 共享同一个元表)。

完整代码


#include <assert.h>
#include <lauxlib.h>
#include <lua.h>
#include <lualib.h>
#include <stdio.h>

typedef struct {
    int count;
} Counter;

static int get_counter(lua_State *L) {
    Counter *c = (Counter *)lua_newuserdatauv(L, sizeof(Counter), 0);
    c->count = 0;
    return 1;
}

static int inc_counter(lua_State *L) {
    Counter *c = (Counter *)lua_touserdata(L, 1);
    int n = (int)luaL_optinteger(L, 2, 1);
    c->count += n;
    lua_pushinteger(L, c->count);
    return 1;
}

static int get_count(lua_State *L) {
    Counter *c = (Counter *)lua_touserdata(L, 1);
    lua_pushinteger(L, c->count);
    return 1;
}

int main(void) {
    lua_State *L = luaL_newstate();
    luaL_openlibs(L);

    lua_pushcfunction(L, get_counter);
    lua_setglobal(L, "get_counter");
    lua_pushcfunction(L, inc_counter);
    lua_setglobal(L, "inc_counter");
    lua_pushcfunction(L, get_count);
    lua_setglobal(L, "get_count");

    const char *script = "local c = get_counter()\n"
                         "print('initial:', get_count(c))\n"
                         "print('inc 5:', inc_counter(c, 5))\n"
                         "print('inc 1:', inc_counter(c))\n"
                         "c = nil\n"
                         "collectgarbage('collect')\n"
                         "print('done')\n";

    luaL_dostring(L, script);

    lua_close(L);
    puts("07-userdata: ok");
    return 0;
}