目录
一、概述
二、源码实现
相关函数实现
1,lua_pushlightuserdata
2,lua_newuserdata
3,luaC_newobj(lgc.c)
4,createstrobj(lstring.c)
三、总结
Lua中userdata分两类:
1,是轻量级userdata(light userdata),轻量级userdata是一种表示C指针的值,对Lua虚拟机来说,这种数据类型不需要GC(垃圾回收),其指向的内存由用户分配和释放,其实现就是一个void *p指针。
实现:lightuserdata通过LUA的API(lua_pushlightuserdata())创建,返回一个指针,自己管理分配和回收。
2,userdata类型完全userdata(full userdata),内存是由Lua虚拟机分配,并有GC机制负责处理。
实现:userdata通过Lua的API(lua_newuserdata())分配内存,就像C里的malloc()函数分配内存,但不需要调用free()去释放内存,该内存是由LUA的GC机制进行回收。
userdata内存存储形式类似于字符串,以一个头开始然后紧随头后面保存相关的数据,但是每个userdata都拥有自己独立的元表。下面是userdata对应的头数据结构Udata代码(lobject.h):
431 /*
432 ** Header for userdata; memory area follows the end of this structure
433 */
434 typedef union Udata {
435 L_Umaxalign dummy; /* ensures maximum alignment for `local' udata */
436 struct {
437 CommonHeader;
438 struct Table *metatable;
439 struct Table *env;
440 size_t len; /* number of bytes */
441 } uv;
442 } Udata;
成员dummy和CommonHeader含义与TString完全一样。其他成员含义如下:
struct Table *metatable:指向userdata对应的元表,也就是一个table。可以调用luaL_newmetatable创建一个元表(注意是在注册表上创建的,也就是说userdata的元表保存在注册表中),然后调用lua_setmetatable来设置userdata的元表,注意Lua代码中不能改变userdata的元表。
struct Table *env:userdata的环境,创建userdata时该值为NULL。可以通过调用lua_setuservalue函数来设置userdata的环境。
size_t len:保存userdata内存的大小,就紧随头后面数据内存的大小。
userdata的数据部分和字符串一样,都是紧接着放在结构后面,创建userdata的代码如下(lstring.c):
175 Udata *luaS_newudata (lua_State *L, size_t s, Table *e) {
176 Udata *u;
177 if (s > MAX_SIZET - sizeof(Udata))
178 luaM_toobig(L);
179 u = &luaC_newobj(L, LUA_TUSERDATA, sizeof(Udata) + s, NULL, 0)->u;
180 u->uv.len = s;
181 u->uv.metatable = NULL;
182 u->uv.env = e;
183 return u;
184 }
LUA_API void lua_pushlightuserdata (lua_State *L, void *p) {
lua_lock(L);
setpvalue(L->top, p);
api_incr_top(L);
lua_unlock(L);
}
LUA_API void *lua_newuserdata (lua_State *L, size_t size) {
Udata *u;
lua_lock(L);
luaC_checkGC(L);
u = luaS_newudata(L, size, NULL);
setuvalue(L, L->top, u);
api_incr_top(L);
lua_unlock(L);
return u + 1;
}
/*
** create a new collectable object (with given type and size) and link
** it to '*list'. 'offset' tells how many bytes to allocate before the
** object itself (used only by states).
*/
GCObject *luaC_newobj (lua_State *L, int tt, size_t sz, GCObject **list,
int offset) {
global_State *g = G(L);
char *raw = cast(char *, luaM_newobject(L, novariant(tt), sz));
GCObject *o = obj2gco(raw + offset);
if (list == NULL)
list = &g->allgc; /* standard list for collectable objects */
gch(o)->marked = luaC_white(g);
gch(o)->tt = tt;
gch(o)->next = *list;
*list = o;
return o;
}
/*
** creates a new string object
*/
static TString *createstrobj (lua_State *L, const char *str, size_t l,
int tag, unsigned int h, GCObject **list) {
TString *ts;
size_t totalsize; /* total size of TString object */
totalsize = sizeof(TString) + ((l + 1) * sizeof(char));
ts = &luaC_newobj(L, tag, totalsize, list, 0)->ts;
ts->tsv.len = l;
ts->tsv.hash = h;
ts->tsv.extra = 0;
memcpy(ts+1, str, l*sizeof(char));
((char *)(ts+1))[l] = '\0'; /* ending 0 */
return ts;
}
1、lua中的userdata类型主要用来表示在C/C++中定义的类型,即用来实现扩展lua,这些扩展代码通常是用C/C++来实现的。对lua 虚拟机来说userdata只是提供了一块原始的内存区域,可以用来存储任何东西,并且在lua中userdata没有任何预定义的操作。注意这块分配的额外内存是由Lua垃圾收集器来管理的,无须关心起释放等情况。
2、userdata的元表通常用来存储对应C/C++类型的方法,这样在脚本中可以直接调用userdata对应的方法,并且这些方法也是用C/C++实现的。
3、创建一个新的userdata时,其环境表默认值是空,即成员值env 为NULL,当前这个成员在Lua没被使用。在一些文章指出,这个成员可以保存与userdata实例相关的数据,而其对应的元表保存userdata的方法。
参考资料:
Lua 5.2.1源码
https://blog.csdn.net/MaximusZhou/article/details/45063619
https://blog.csdn.net/fwb330198372/article/details/82217022?utm_medium=distribute.pc_relevant.none-task-blog-searchFromBaidu-13.control&dist_request_id=&depth_1-utm_source=distribute.pc_relevant.none-task-blog-searchFromBaidu-13.control