本文内容基于版本:Lua 5.3.0
概述
Userdata在储存形式上和字符串十分类似,也是在代表该数据类型的结构体Udata后面直接追加数据内容部分。Userdata可以看成是拥有独立元表,没有内部化处理,也不需要追加'\0'字符的字符串 。从底层来看,Userdata和字符串存储的都是二进制数据,因此它们必然有一定的共同性,而由于两者用途不同又展现出一定的差异性。阅读Lua源码可以看到Userdata和字符串的实现代码被放在一起, 两者的API也以luaS打头。
Udata结构
? Udata结构的声明
Lua中Userdata对应的C结构为Udata,该类型定义在lobject.h中。
// lobject.h
/
Common Header for all collectable objects (in macro form, to be
included in other objects)
/
#define CommonHeader GCObject next; lu_byte tt; lu_byte marked
// lobject.h
/
Header for userdata; memory area follows the end of this structure
(aligned according to 'UUdata'; see next).
/
typedef struct Udata {
CommonHeader;
lubyte ttuv; / user value's tag /
struct Table metatable;
sizet len; / number of bytes /
union Value user; / user value /
} Udata;
CommonHeader : 用于GC的信息。
ttuv :
metatable : Userdata关联的元表。
len :
user :
? Udata存储结构图
Lua中Userdata数据内容部分并未分配独立的内存来存储,而是直接追加在Udata结构的后面。Udata存储结构如下图:
? Userdata对象 = Udata结构 + 实际用户数据
? Udata结构 = GCObject 指针 + Userdata信息数据
创建Userdata
? Userdata创建的函数调用图
Userdata创建过程的核心函数是luaS_newudata,而函数lua_newuserdata作为luaS_newudata的包裹函数旨在屏蔽Udata的内部细节,而只提供给用户指向存储实际数据部分的内存指针。下面就这两个函数着重进行分析。
? luaS_newudata
// lstring.h
#define sizeludata(l) (sizeof(union UUdata) + (l))
// lstring.h
LUAI_FUNC Udata luaS_newudata (lua_State L, size_t s);
// lstring.c
Udata luaS_newudata (lua_State L, size_t s) {
Udata u;
GCObject *o;
if (s > MAX_SIZE - sizeof(Udata))
luaM_toobig(L);
o = luaC_newobj(L, LUA_TUSERDATA, sizeludata(s));
u = gco2u(o);
u->len = s;
u->metatable = NULL;
setuservalue(L, u, luaO_nilobject);
return u;
}//代码效果参考:http://www.ezhiqi.com/bx/art_6973.html