注:本篇文章从个人博客园移植而来
_G
lua的全局变量保存在一个常规的table中,这个table被称为全局环境,该table存储在名为 _G 的表中
for i, v in pairs(_G) do
print(i)
end
--[[
-- 为了方便查看,进行了缩行
rawequal getmetatable bit32 load dofile pairs table package loadfile
io xpcall print type _G debug rawlen tostring error assert rawget
require math select next utf8 coroutine _VERSION arg string os rawset
tonumber collectgarbage setmetatable pcall ipairs
]]
--[[
-- 整理了部分,方便大家学习Lua C相关
------------------ lbaselib.c中 ------------------
参考文件网址:http://www.lua.org/source/5.3/lbaselib.c.html
assert collectgarbage dofile error getmetatable ipairs loadfile
load next pairs pcall print rawequal rawget rawset select
setmetatable tonumber tostring type xpcall _G _VERSION
------------------ loadlib.c中 ------------------
参考文件网址:http://www.lua.org/source/5.3/loadlib.c.html
require module
------------------ lualib.h 中 ------------------
参考文件网址:http://www.lua.org/source/5.3/lualib.h.html
coroutine table io os string math debug package
------------------ lstrlib.h 中 ------------------
参考文件网址:http://www.lua.org/source/5.3/lstrlib.c.html
unpack
]]
全局变量不需要声明即可使用,然而打字出错的情况可能会导致程序出现难以发现的bug。
因此我们可以添加一个对全局表不存在健的访问,提高程序的健全性:
print(g_A) -- nil 可能会导致程序出现难以发现的bug
-- 推荐方式
setmetatable(_G, {
-- 写入变量
__newindex = function(_, n)
error("attempt to write to undeclared variable " .. n, 2)
end,
-- 读取变量
__index = function(_, n)
error("Error: attempt to read undeclared variable " .. n, 2)
end,
})
print(g_A) -- Error:attempt to read undeclared variable g_A
Lua虚拟机
在Lua虚拟机中,用一个全局结构的global_State来管理多个lua_State。
在调用luaL_newstate时,创建一个全局的global_State和一个lua_State后,luaL_newstate会调用f_luaopen,然后f_luaopen调用init_registry来初始化注册表。
/*
** Create registry table and its predefined values
** 在lstate.c中,参考代码网址:http://www.lua.org/source/5.1/lstate.c.html
*/
static void init_registry (lua_State *L, global_State *g) {
TValue temp;
/* 创建注册表,初始化注册表数组部分大小为LUA_RIDX_LAST */
Table *registry = luaH_new(L);
sethvalue(L, &g->l_registry, registry);
luaH_resize(L, registry, LUA_RIDX_LAST, 0);
/* 把这个注册表的数组部分的第一个元素赋值为主线程的状态机L
这里所说的线程并非是os的线程,而是lua的状态机概念
*/
/*即 registry[LUA_RIDX_MAINTHREAD] = L */
setthvalue(L, &temp, L); /* temp = L */
luaH_setint(L, registry, LUA_RIDX_MAINTHREAD, &temp);
// 把注册表的数组部分的第二个元素赋值为全局表
/*即 registry[LUA_RIDX_GLOBALS] = table of globals */
/* temp = new table (global table) */
sethvalue(L, &temp, luaH_new(L));
luaH_setint(L, registry, LUA_RIDX_GLOBALS, &temp);
}
在创建注册表后,会将全局表注册到脚本中。
// 在linit.c中,参考代码网址:http://www.lua.org/source/5.1/linit.c.html
static const luaL_Reg loadedlibs[] = {
{"_G", luaopen_base},
{LUA_LOADLIBNAME, luaopen_package},
{LUA_COLIBNAME, luaopen_coroutine},
{LUA_TABLIBNAME, luaopen_table},
{LUA_IOLIBNAME, luaopen_io},
{LUA_OSLIBNAME, luaopen_os},
{LUA_STRLIBNAME, luaopen_string},
{LUA_MATHLIBNAME, luaopen_math},
{LUA_UTF8LIBNAME, luaopen_utf8},
{LUA_DBLIBNAME, luaopen_debug},
#if defined(LUA_COMPAT_BITLIB)
{LUA_BITLIBNAME, luaopen_bit32},
#endif
{NULL, NULL}
};
以luaopen_base为例,会吧脚本用的函数注册到全局表,代码如下:
// 相关的函数表
static const luaL_Reg base_funcs[] = {
{"assert", luaB_assert},
{"collectgarbage", luaB_collectgarbage},
{"dofile", luaB_dofile},
{"error", luaB_error},
{"getmetatable", luaB_getmetatable},
{"ipairs", luaB_ipairs},
{"loadfile", luaB_loadfile},
{"load", luaB_load},
/* 省略了部分代码*/
{"_G", NULL},
{"_VERSION", NULL},
{NULL, NULL}
};
//
LUAMOD_API int luaopen_base (lua_State *L) {
/* open lib into global table */
lua_pushglobaltable(L);
luaL_setfuncs(L, base_funcs, 0);
/* set global _G */
lua_pushvalue(L, -1);
lua_setfield(L, -2, "_G");
/* set global _VERSION */
lua_pushliteral(L, LUA_VERSION);
lua_setfield(L, -2, "_VERSION");
return 1;
}