博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Lua中的全局变量与环境
阅读量:5752 次
发布时间:2019-06-18

本文共 4097 字,大约阅读时间需要 13 分钟。

hot3.png

环境的概念

Lua中类型为thread,function和userata的对象都可以关联一个表,称之为环境。环境也是一个常规的table。可以和普通的table一样进行操作,存放与对象相关的各种变量。

  • 关联的thread上的环境只能通过C代码中访问。
  • 关联在userdata上的环境在 Lua 中没有意义。 这个东西只是为了在程序员想把一个表关联到一个 userdata 上时提供便利。
  • 关联在function上的环境用来接管本函数内全局变量的访问。

全局变量

Lua中的全局变量存在放当前函数的环境中,Lua标准库中的函数如setmetable, string.find等注册在函数的环境中,在Lua脚本就可以直接访问。

操作函数的环境

Lua代码中可以访问和操作和函数关联的环境,Lua标准库为此提供了两个方法

  • getfenv 获取当前函数的环境
  • setfenv 设置当前对象函数关联的环境

查看所有的全局变量

通过getfenv(0)获取当前环境后遍历,可以查看所有的全局变量。

for k, v in pairs(getfenv(0)) do    print ("k: ", k, ", v: ", type(v))end

定义全局变量与局部变量

Lua中定义的变量默认为全局变量,保存在当前函数的环境中。 如下面的代码

local env = getfenv(1)print("var: ", env["var"])print("loval_var: ", env["loval_var"])var = "Hello, global variable"local local_var = "Hell, local variable"print("after set")print("var: ", env["var"])print("loval_var: ", env["loval_var"])

变量var为全局变量,存放在当前的环境中,通过getfenv().var可以访问,而变量local_var为局部变量,getfenv().local_var值为nil

执行结果

$ lua test3.luavar: 	nilloval_var: 	nilafter setvar: 	Hello, global variableloval_var: 	nil

嵌套函数的环境

在thread中通过load等方式创建的函数称为非嵌套函数,非嵌套函数的默认环境为此thread的环境。在函数中创建函数时,会将自己的环境设置为新创建的函数的默认环境。

访问全局变量时访问的是当前所在函数的环境,如下面的代码

local function f()    print("env: ", getfenv())    local function f1()        print("env f1: ", getfenv())    end    local function f2()        print("env f2: ", getfenv())    end    f1()    f2()endf()

执行结果 f,f1,f2三个函数的环境是同一个table

env: 	table: 0x25a16b0env f1: 	table: 0x25a16b0env f2: 	table: 0x25a16b0

改变函数的环境

因为函数f1和f2都是在函数f中创建的,他们的环境的初始值就是函数f的环境。 下面通过setfenv改变函数的环境

local function f()    print("env: ", getfenv())    local function f1()        print("env f1: ", getfenv())    end    local function f2()        print("env f2: ", getfenv())    end        setfenv(f2, {})    f1()    f2()endf()

执行结果如下,getfenv本身就在环境中存放,通过setfenv(f2, {})将函数f2的环境设置成一个空的table,在函数f2中调用getfenv时由于getfenv是nil,导致脚本报错。

env: 	table: 0x17a66b0env f1: 	table: 0x17a66b0lua: test4.lua:9: attempt to call global 'getfenv' (a nil value)stack traceback:	test4.lua:9: in function 'f2'	test4.lua:15: in function 'f'	test4.lua:18: in main chunk	[C]: ?

将函数f的环境通过闭包保存下来,就可以在函数f2中调用其中的方法。如下所示

local function f()    print("env: ", getfenv())    local function f1()        print("env f1: ", getfenv())    end        local env = getfenv()    local function f2()        local e = env        e.print("env f2: ", e.getfenv())    end        setfenv(f2, {})    f1()    f2()endf()

此时可以看到函数f2的环境与函数f和f1不同

$ lua test4.lua env: 	table: 0x1ebf6b0env f1: 	table: 0x1ebf6b0env f2: 	table: 0x1ec7480

_G

Lua中的_G是一个指向全局环境(thread的环境)的全局变量。详细一点可以这样理解

  1. thread有一个环境,称为全局环境
  2. 全局环境中有一个变量,变量名为_G, 值为全局环境(_G._G = _G?)
  3. thread中调用load(string)或者loadstring等,解析Lua代码,生成一个函数
  4. 生成的函数的环境默认为thread的环境
  5. 在非嵌套函数中使用_G如print(_G), 打印的是全局环境,也是此函数的环境。

输出_G和_G._G,值完全相同

$ cat test.lua print(_G)print(_G._G)$ lua test.luatable: 0x1d0c6b0table: 0x1d0c6b0

如果通过setfenv设置了环境,新的环境是没有_G这个变量的。

local function f()    local function f1()        print("f1: ", _G)    end        local env = getfenv()    local function f2()        local e = env        e.print("f2: ", _G)    end        setfenv(f2, {})    f1()    f2()endf()

执行结果

f1: 	table: 0xf916b0f2: 	nil

GETGLOBAL 和 lua_getglobal的迷惑

调用print输出字符串print("Hello"),可以通过luac查看编译后的lua虚拟机的指令

$ luac -l -l test.luamain 
(4 instructions, 16 bytes at 0xd92530)0+ params, 2 slots, 0 upvalues, 0 locals, 2 constants, 0 functions 1 [1] GETGLOBAL 0 -1 ; print 2 [1] LOADK 1 -2 ; "Hello" 3 [1] CALL 0 2 1 4 [1] RETURN 0 1constants (2) for 0xd92530: 1 "print" 2 "Hello"locals (0) for 0xd92530:upvalues (0) for 0xd92530:

这里只关注GETGLOBAL这条指令,print是全局变量,需要通过GETGLOBAL取到这个变量的值

对于GETGLOBAL,Lua虚拟机的执行如下(以lua5.1为例),在函数luaV_execute中

case OP_GETGLOBAL: {        TValue g;        TValue *rb = KBx(i);        sethvalue(L, &g, cl->env);        lua_assert(ttisstring(rb));        Protect(luaV_gettable(L, &g, rb, ra));        continue;      }

注意sethvalue(L, &g, cl->env);这句,cl代表当前的函数,cl->env指向当前函数的环境。即GETGLOBAL指令取得的是从当前函数的环境中取得变量的值的。

而lua_getglobal这个函数确是直接访问thread的全局环境,取得全局环境中key为name的值。

void lua_getglobal (lua_State *L, const char *name);

同样都是"getglobal",却是两个不同的意思,直接让我迷惑了很久,差点分不清什么是全局变量了。

转载于:https://my.oschina.net/u/2539854/blog/857968

你可能感兴趣的文章
AC旁挂三层交换机管理ap,二层接入ap心得
查看>>
DC/OS虚拟网络
查看>>
如何将Exchange 2010 CU15更新到CU26 - 解决CVE安全漏洞
查看>>
JS中比较数字大小
查看>>
POST中文转码问题
查看>>
springcloud 学习-eureka搭建-为eureka添加认证
查看>>
jQuery插件的开发
查看>>
基础,基础,还是基础之JAVA基础
查看>>
如何成为一个C++高级程序员
查看>>
iptables 生产环境配置
查看>>
ant android 打包签名和渠道
查看>>
linux命令学习(1)-awk
查看>>
一个简单的接口,被调用并同步给出响应的方法
查看>>
Hadoop序列化与压缩
查看>>
由“男怕入错行”说开去
查看>>
php-fpm多实例运行
查看>>
CGImageSource对图像数据读取任务的抽象
查看>>
我的友情链接
查看>>
xss test
查看>>
也谈svn分支与合并
查看>>