gpt4 book ai didi

lua - 全局变量_G有多特殊?

转载 作者:行者123 更新时间:2023-12-04 11:35:29 26 4
gpt4 key购买 nike

摘录自Lua 5.3 manual

_G
保存全局环境的全局变量(不是函数)(请参见§2.2)。 Lua本身不使用此变量。更改其值不会影响任何环境,反之亦然。

§2.2的相关部分

[…]每个块都在名为_ENV的外部局部变量的范围内编译,因此_ENV本身绝不是块中的自由名称。
[…]
任何用作_ENV值的表都称为环境。
Lua保留了一个称为全球环境的杰出环境。此值保存在C注册表中的特殊索引处。在Lua中,全局变量_G用相同的值初始化。 (_G从未在内部使用。)
当Lua加载一个块时,其_ENV上值的默认值是全局环境。因此,默认情况下,Lua代码中的自由名称引用全局环境中的条目

我知道对于每个加载的块,由于_ENV将是第一个升值,因此它指向全局环境表,由_G指向load

> =_G, _ENV
table: 006d1bd8 table: 006d1bd8

确认两个都指向同一个表。该手册多次声明, _ENV_G只是常规名称,没有隐藏的含义,并且Lua本身不在内部使用它。我在下面尝试了这个块:
local a = { }
local b = a -- since tables are objects, both refer to the same table object
print(a, b) -- same address printed twice
a = { } -- point one of them to a newly constructed table
print(a, b) -- new, old table addresses printed

现在对 _G_ENV做同样的事情:
local g = _G          -- make an additional reference
print(g, _G, _ENV) -- prints same address thrice
local p = print -- backup print for later use
_ENV = { } -- point _ENV to a new table/environment
p(g, _G, _ENV) -- old, nil, new

table: 00ce1be0 table: 00ce1be0 table: 00ce1be0
table: 00ce1be0 nil table: 00ce96e0

如果 _G是普通的全局变量,为什么在这里变成 nil?如果引用计数已完成,则 _G在发布 _ENV时仍保留引用。像上面的 b一样,它也应该保留旧表,不是吗?
但是,对于以下块, _G不变/保留!
_ENV = { _G = _G }
_G.print(_G, _ENV, _ENV._G) -- old, new, old

但是这里被杀死了:
_ENV = { g = _G }
_ENV.g.print(_ENV, _ENV.g, _G) -- new, old, nil

保留它的另一种情况:
print(_G, _ENV)                       -- print same address twice
local newgt = {} -- create new environment
setmetatable(newgt, {__index = _G}) -- set metatable with _G as __index metamethod
_ENV = newgt -- point _ENV to newgt
print(_G, newgt, _ENV) -- old, new, new

_G的行为有很多变化,因此手册给出的最初保证似乎有些不稳定。我在这里想念什么?

最佳答案

全局变量_G有多特殊?

它以三种方式很特殊:


它使用名称reserved for internal use by Lua
它是由Lua的标准模块之一(特别是
"base"模块)。如果您创建一个新的lua_State而没有
打开“基本”模块,您将没有_G变量。的
独立解释器已加载了所有标准库,
虽然。
一些第三方Lua模块使用全局变量_G,并且
更改/删除它可能会破坏这些模块。


_G有什么意义?

Lua中的全局变量是使用普通表实现的。任何
访问不是local变量或上限值的变量
重定向到该表。局部变量始终具有优先权,因此
如果您有一个具有相同名称的全局变量和局部变量,
您将永远得到本地的。 _G在这里起作用:如果
如果需要全局变量,可以说_G.name而不是name
假设名称_G不是局部变量(保留给Lua,
还记得吗?!),这将始终为您提供全局变量的值
通过使用表索引语法,从而消除与
局部变量名称。在较新的Lua版本(5.2+)中,您也可以使用
_ENV.name作为替代,但_G早于这些版本,并且
保持兼容性。

在其他情况下,您想掌握全局变量
桌子,例如用于设置一个元表。 Lua允许您自定义
表的行为(和其他值),方法是使用
setmetatable函数,但是您必须将表作为
参数。 _G可以帮助您做到这一点。

在某些情况下,如果您向全局表添加了一个元表
您可能想规避元方法(__index和/或
__newindex)刚刚安装。您可以使用rawget
rawset,但是您需要将globals表作为参数传递
也一样

请注意,上面列出的所有用例仅适用于Lua代码,不适用于
C代码。在C代码中,您没有命名局部变量,仅堆栈
索引。因此没有歧义。如果您想参考
全局表传递给某些功能,可以使用
lua_pushglobaltable(用uses the registry代替_G)。
结果,用C实现的模块不需要/不需要_G
全局变量。这适用于Lua的标准库(
同样在C)中实现。实际上,参考手册
guarantees,即未使用_G(变量,而不是表)
由Lua或其标准库提供。

_G_ENV有何关系?

从5.0版开始,Lua允许您更改用于查找的表
基于(Lua)函数的全局变量。在Lua 5.0和5.1中
您为此使用了setfenv函数(globals表是
也称为“功能环境”,因此命名为setfenv。 Lua 5.2
引入了使用另一个特殊变量名称_ENV的新方法。
虽然_ENV不是全局变量,但Lua确保每个
块以_ENV升值开头。新方法通过让
Lua将对非本地(和非上值)变量的所有访问权限转换为
a命名为_ENV.a。无论什么时候_ENV在代码中
习惯于解析全局变量。这样更安全,因为
您无法更改未编写自己的代码的环境
(无需使用调试库),并且也更加灵活,因为您
可以通过创建更改单个代码块的环境
local _ENV范围有限的变量。

但是,无论如何,您都需要使用默认环境
在程序员有机会设置自定义变量之前(或者如果程序员
不想更改它)。在启动时,Lua创建此默认值
您和商店的环境(也称为"global environment"
registry中。此默认环境用作
除非您将自定义环境传递到,否则所有块的_ENV增值
loadloadfile。也lua_pushglobaltable
直接从注册表中检索此全局环境,因此
所有C模块都会自动使用它来访问全局变量。

如果已加载标准的“基本” C模块,则此
默认的“全局环境”具有一个名为_G的表字段,该字段引用
回到全球环境。

把它们加起来:


全局变量_G实际上是_ENV._G
_ENV不是全局变量,而是上限值或局部变量。
默认“全局环境”的_G字段指向
全球环境。
默认情况下,_G_ENV引用同一表(表示全局
环境)。
C代码既不使用,也不使用注册表中的一个字段(
再次根据定义指向全球环境)。
您可以在全局环境中替换_G(在全局环境中)
C模块或Lua本身(但您可能会破坏第三方Lua
模块(如果不小心)。
您可以随时替换_ENV,因为它只会影响
您自己的代码(最多当前块/文件)。
如果替换_ENV,则可以自行决定是否_G
_ENV._G)将在受影响的代码中显示
指着。

关于lua - 全局变量_G有多特殊?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35910099/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com