Lua5.3 注册表 _G _ENV
注明来源:http://blog.csdn.net/murisly/article/details/46518551
注册表的描述,借用PIL中的一段话:
registry 一直位于一个由 LUA_REGISTRYINDEX 定义的值所对应的假索引(pseudo-index)的位置。一个假索引除了他对应的值不在栈中之外,其他都类似于栈中的索引。Lua API 中大部分接受索引作为参数的函数,也都可以接受假索引作为参数—除了那些操作栈本身的函数,比如 lua_remove,lua_insert。例如,为了获取以键值 "Key" 保
存在 registry 中的值,使用下面的代码:
- lua_pushstring(L, "Key");
- lua_gettable(L, LUA_REGISTRYINDEX);
由于这个表是所有的lua库所共享的,所以key值也一定要注意。云大也给了一些去key值的参考方法。
函数中,取注册表键值有这样的代码,可以看出注册表存储在 global_State 结构的 l_registry 变量中
- static TValue *index2addr (lua_State *L, int idx)
- else if (idx == LUA_REGISTRYINDEX) /*注册表索引*/
- return &G(L)->l_registry;
注册表这个量在lua CApi可以访问,lua脚本中访问不了。Lua中可以访问的是 _G 这个全局变量。
- for i,v in pairs(_G) do
- print("name:", i, ", type:", type(v));
- end
这可以看到当前全局量里面所包含的值。那么全局量保存在那个位置呢?可以在lua_getglobal的源代码
- LUA_API void lua_getglobal (lua_State *L, const char *var) {
- Table *reg = hvalue(&G(L)->l_registry);
- const TValue *gt; /* global table */
- lua_lock(L);
- gt = luaH_getint(reg, LUA_RIDX_GLOBALS);
- setsvalue2s(L, L->top++, luaS_new(L, var));
- luaV_gettable(L, gt, L->top - 1, L->top - 1);
- lua_unlock(L);
- }
可以看出获得全局变量,先是获得注册表的值,然后注册表中key为 LUA_RIDX_GLOBALS 的表就是全局表。Lua.h中定义了这个宏。
- #define LUA_RIDX_MAINTHREAD 1
- #define LUA_RIDX_GLOBALS 2
- #define LUA_RIDX_LAST LUA_RIDX_GLOBALS
当前也就是构建了这样的一个状态
再来看看 _ENV 这个量
Print(_G); -->table:003C27D8
Print(_ENV); -->table:003C27D8
这里看出两个指向的是同一个table。那么这两个是什么关系呢?Lua官方说明文档中:
When Lua loads a chunk, the default value for its _ENV upvalue is the global environment (see load).
Therefore, by default, free names in Lua code refer to entries in the global environment
(and, therefore, they are also called global variables).
Moreover, all standard libraries are loaded in the global environment and some functions there operate on that environment.
You can use load (or loadfile) to load a chunk with a different environment.
(In C, you have to load the chunk and then change the value of its first upvalue.)
关于chuck的解释:
The unit of compilation of Lua is called a chunk. Syntactically, a chunk is simply a block。
A chunk can be stored in a file or in a string inside the host program.
To execute a chunk, Lua first loads it, precompiling the chunk's code into instructions for a virtual machine,
and then Lua executes the compiled code with an interpreter for the virtual machine.
一个chunk就是lua的一个解释单元,可以存储在文件或者字符串中。对于每一个chunk,都有一个叫_ENV的upvalue,此时_ENV的初值就是_G。在chunk内的函数,都会有这个upvalue值。修改当前的chunk的 _ENV,也就修改了_G,那么在该代码块中加入的非local变量,可以直接通过名称在其他chunk中访问到(当然该chunk的_ENV 也得是 _G)。
所以不规范得命名很容易影响其他模块。为了避免这种情况,变量尽量申请为local类型。利用_G和表元表和元方法,即可以强制声明全局变量。
- local oldprint = print;
- local oldload = load;
- local oldpairs = pairs;
- local _tenv = {};
- _tenv.tprint = print;
- _tenv.tpairs = pairs;
- oldprint(_ENV); -->table: 00A888D8
- oldprint(_ENV._G); -->table: 00A888D8
- oldprint(_G); -->table: 00A888D8
- local _tg = _G; -->_G 通过 _tg 保存起来
- _G = {};
- _ENV = _tenv;
- x = 1;
- for i,v in tpairs(_ENV) do
- tprint(i); -->tprint
- end -->x
- -->tpairs
- oldprint(_ENV); -->table: 00A8E660
- oldprint(_G); -->nil(此时这两个表指向了不同的地方)
- --test这个chunk内,使用的 _ENV 是 _tenv。这里 _ENV 添加了变量 y
- local a = oldload("y = 1; for i,v in tpairs(_ENV) do tprint(i); end", "test", "t", _tenv);
- a();
- --访问另一个chunk,使用同样的 _ENV , y值可以直接访问
- local b = oldload("y = y + 1;", "test", "t", _tenv);
- b();
- oldprint(_tenv.y); -->2
- oldprint(_ENV.y); -->2
- oldprint(_tg.y) -->nil
这里可以看出,当前chunk 的 _ENV 改变了,不会改变全局的 _G(除非此时 _ENV 就是指向的 _G)。在加载一个chunk的时候可以设置 _ENV,说明可以让chunk在特定的环境中运行。获取一个独立运行环境的函数,可以修改 _ENV 。
- function testbar(env)
- local _ENV = env;
- _ENV.x = 1;
- return function ()
- return _ENV.x;
- end
- end
- local env = {};
- local f = testbar(env);
- print(_ENV.x);
- print(f());
注明来源:http://blog.csdn.net/murisly/article/details/46518551