1.lua_gettable
void lua_gettable (lua_State *L, int index);
把t[k]
值压入堆栈,这里的 t
是指有效索引 index
指向的值,而 k
则是栈顶放的值。这个函数会弹出堆栈上的 key,把结果放在栈上相同位置。
下面举个例子:
// 将一个key放到栈顶,这个key为1。如果你的key是字符串,那就用lua_pushstring。 lua_pushnumber(L, 1); // table一开始是在栈顶,即-1处的,但上面的语句压入了一个值,栈顶变-2了。 // lua_gettable的作用就是以栈顶的值作为key来访问-2位置上的table。 lua_gettable(L, -2);
这时table中的第1个元素的值就放到栈顶了,你想怎么使用就怎么使用吧。
获取table元素:
* 将元素的key压入到栈中,用 lua_gettable(Lua_state,index)
* 对于字符串索引,可以用lua_getfield(Lua_state,index,key)来直接获取,
如:lua_getfield(stack, -1, "loaded");等价于 lua_pushstring(L,"loaded") lua_gettable(L,-2);
上面说的是访问table中的一个元素的方法,那要怎么样遍历table中的所有元素呢?
1)如果table是一个以连续的整形作为key的table, 可以用下面方法:
int size = lua_objlen(L,-1);//相关于#table for(int i = 1; i <= size; i++) { lua_pushnumber(L, i); lua_gettable(L, -2); //这时table[i]的值在栈顶了 lua_pop(L, 1);//把栈顶的值移出栈,保证栈顶是table以便遍历。 };
2)如果table中的key是任意值呢?可以用下面的方法:
lua_pushnill(L); while(lua_next(L, -2)) { //这时值在-1(栈顶)处,key在-2处。 lua_pop(L, 1);//把栈顶的值移出栈,让key成为栈顶以便继续遍历 }
这里重点说明一下lua_next。
它执行操作是这样的,以栈顶元素为key,先判断上一个key的值(这个值放在栈顶,如果是nil,则表示当前取出的是table中第一个元素的值),然后得到当前的key和value。
这时先把栈顶出栈,将新key进栈,后将value进栈。这样栈顶就是table中第一个遍历到的元素的值。用完这个值后,我们要把这个值出栈,让新key在栈顶以便继续遍历。当根据上一个key值算不出下一个key值时(其实这时候key的是多少并不重要,只要不为nil就行,因为为nil会返回table的第一个元素),lua_next返回0,结束循环。
2.lua_settable
void lua_settable (lua_State *L, int index);
作一个等价于 t[k] = v
的操作, 这里 t
是一个给定有效索引 index
处的值, v
指栈顶的值, 而 k
是栈顶之下的那个值。
这个函数会把键和值都从堆栈中弹出。
其实这个解释的意思就是,lua_settable 会把栈顶作为value,栈顶的下一个作为key设置到index指向的table,最后把这两个弹出弹出栈,这时候settable完成。
3:_G分析
在Lua脚本层,Lua将所有的全局变量保存在一个常规的table中,这个table被称为全局环境,并且将这个table保存在一个全局变量_G中,也就是说在脚本中可以用_G获取这个全局table,并且有_G._G == _G,在默认情况,Lua在全局环境_G中添加了标准库比如math、函数比如pairs等。
lua_pushglobaltable把这个全局环境压入栈中,在lua5.2 该函数实质就是从注册表中获取这个全局环境,即lua_pushglobaltable用下面宏定义的:#define lua_pushglobaltable(L) lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS)
这里的LUA_REGISTRYINDEX是Lua注册表(注册表是lua虚拟机范围内是全局唯一的)的伪索引,LUA_RIDX_GLOBALS是全局环境在注册表中的索引(也就说,全局环境_G是虚拟机范围内是全局唯一的)。
4:lua_pushliteral和lua_pushstring有何区别?
通常在push字符串字面值时使用lua_pushliteral,在push字符串指针是使用lua_pushstring。
原因是前者通过sizeof(字符串字面值)/sizeof(char)计算长度,而后者通过strlen计算长度。
因此前者只能push字符串字面值,但速度比后者快。而后者既可push字符串字面值,也可push字符串指针。
5:luaL_newmetatable
在lua代码中的普通表,不能作为userdata的metatable。必须使用luaL_newmetatable创建的表才能作为userdata的metatable。
6:向lua中注册c函数的过程是通过lua_pushcclosure(L, f, n)函数实现的
1. 创建一个 sizeof(CClosure) + (n - 1) * sizeof(TValue)大小的内存, 这段内存是 CClosure + TValue[n], 并做gc簿记[这点太重要了, 为什么lua要控制自己世界中的所有变量, 就是因为它要做gc簿记来管理内存], isC= 1 标示其是一个C闭包.
2. c->f = f绑定c函数. --------- 闭包.功能抽象 = f
3. env = 当前闭包的env[这说明了被创建的闭包继承了创建它的闭包的环境]. ----------- 闭包.env = env
4. 把栈上的n个元素赋值到c->upvalue[]数组中, 顺序是越先入栈的值放在upvalue数组的越开始位置, c- >nupvalues指定改闭包upvalue的个数. ---------- 闭包.upvalue = upvalue
5. 弹出栈上n个元素, 并压入新建的Closure到栈顶.