找回密码
 点一下
查看: 9256|回复: 25

[Union Bug]多值多类型对多值多类型的强制类型转换~~替代H2I和I2H的新方案~~

[复制链接]
发表于 2007-12-13 19:37:54 | 显示全部楼层 |阅读模式
算了~~仔细试验之后wc3c那人的理解方法完全不对~~

于是还是删了原来那个帖子~~把我最终的结果重新开个帖子写上来~~

[jass]
globals
      handle H
      integer I
endglobals
[/jass]

[jass]
function Trig_test_Actions takes nothing returns nothing
local integer I
local handle H
set H = GetTriggerUnit()
call BJDebugMsg(I2S(I))
endfunction
[/jass]


这两段代码组合后~~触发动作显示在屏幕上的是什么呢?~0?~

错了~~是触发单位的Handle值~~

很难以置信~~不是嘛?~~

这一问题原是由Wc3c的Troll-Brain发现的~~但他没有正确地了解其原因~~
以下是原帖~~
http://hilton.vs.oiccam.com/showthread.php?t=98230


当全局变量和局部变量同名时~~我们称该全局变量被覆盖~~

然而jass的变量覆盖存在着bug~~

既每执行一次对被覆盖的变量的赋值之后~~当前变量被赋予的值也同时会被无视类型地赋给本函数内所有其余的被覆盖变量~~

也就是说~~在这个函数里~~GetTriggerUnit() 不但被赋给了H~~也被赋给了I~~

我们称这个bug为Union Bug~~是在很多情况下替代以前的Return Bug实现强制类型转换的好方案~~

更多功能见下~~
 楼主| 发表于 2007-12-13 19:41:41 | 显示全部楼层
[trigger]
Sample
    Events
        Player - Player 1 (Red) skips a cinematic sequence
    Conditions
    Actions
        Custom script:   local unit udg_u
        Custom script:   local integer udg_i
        Custom script:   local real udg_r
        Custom script:   local boolean udg_b
        Set u = 圣骑士 0000 <gen>
        Set r = 123.23
        Set b = True
        Set i=1
[/trigger]

以上的动作看似毫无意义~~但如果我们追踪每一步动作中的变量内容变化的话~~就会发现~~

每次对这些被覆盖变量的赋值行为~~都会一并使得其它变量造成影响~~

例如将u设为圣骑士这一句~~同时也将圣骑士的handle值强制赋给了i、r、b~~也就是说~~我们在一句操作中同时达到了将handle强制转换为integer、real和boolean类型的结果!~~

而执行第二句将123.23赋值给r的同时~~也令其它三个被覆盖变量u、i、b的值发生改变~~被强制赋予了123.23这个值~~

下面两句依次类推~~

于是~~利用这个bug~~我们发现了一个极其高效的群体强制数据类型转换的方案~~


然而~~就像我们知道不能用return bug来强制将handle和real转换为string一样~~我们也不能在这个动作里声明一个类型为string的被覆盖变量~~因为一旦声明~~该string类型的变量也会加入这个变化集群中~~当我们对r赋值的时候~~便会造成real被强制转换为string的结果~~而导致崩溃~~

因此~~在执行这些转换操作的时候~~要特别注意不能将real和handle类型的被覆盖变量和string类型的被覆盖变量放到同一个函数中声明~~而需要分在两个函数~~
回复

使用道具 举报

 楼主| 发表于 2007-12-13 19:55:24 | 显示全部楼层
另一方面~~这一bug仅仅会影响到函数中的被覆盖变量~~而其它类型的变量~~不管是全局变量还是局部变量都不会受其影响~~

这样~~我们就可以用覆盖变量来实现类型转换~~用非覆盖变量来记录转换中间值了~~(因为这里的被覆盖变量本身只会记录最后一个值~~因此我们需要正常变量来记录中间结果~~)

这样~~就能实现在同一函数中同时处理多值对多值~~多类型对多类型的功能~~


用途讲解完毕~~
回复

使用道具 举报

 楼主| 发表于 2007-12-13 20:12:10 | 显示全部楼层
最后~~让我们来分析一下这个bug的成因~~以更好地理解它~~

事实上~~根据所有的测试资料~~我得出了一个非常有说服力的可能解释~~


以下作为我解释Union Bug的假说~~



那就是暴雪在设计jass脚本的时候~~试图让jass模仿其它编程语言中被覆盖变量的功能~~

但是实际实现是失败的~~最终结果~~jass仅仅能够支持一个函数拥有一个被覆盖变量~~

而你在同一个函数中声明更多被覆盖变量~~他们占用的仍然是同一个空间~~因此不管你声明的被覆盖变量再多~~它们表现得就像同一个变量一样~~

因此不论你给其中一个被覆盖变量赋何值~~都能从通过其它被覆盖变量将其取出~~


也就是说~~每个函数中所有的被覆盖变量~~之所以表现得形同一人~~其原因正是因为他们都是同一个变量~~只是读取方式不同而已!~~
回复

使用道具 举报

发表于 2007-12-14 10:50:59 | 显示全部楼层
看起来这个规则同样适用于函数参数,全局变量-函数参数的覆盖;
自己画的一个非标准的图示
indicate.JPG
回复

使用道具 举报

发表于 2007-12-14 10:59:35 | 显示全部楼层
[codes=jass]
globals
             handle H
             integer I
endglobals
     
function Trig_test_Actions takes nothing returns nothing
     local handle H=null
     local integer I=0
     set H = GetTriggerUnit()
     call BJDebugMsg(I2S(I))
endfunction
[/codes]
这样写不易引起误解.(这样大家不会被把注意力分散到变量声明顺序上去)
回复

使用道具 举报

发表于 2007-12-14 11:18:56 | 显示全部楼层
很有趣的BUG。使用有一定风险
回复

使用道具 举报

发表于 2007-12-14 12:07:52 | 显示全部楼层
一个小问题,该共用变量空间的归属?(那个图的空间位置看来要改一下,如果说这个空间是由变量名覆盖而在局域内部产生的话)
常规下局域句柄习惯是置null的,共用情况下会如何(比如非handle应该是自动回收了)
回复

使用道具 举报

匿名
匿名  发表于 2007-12-14 12:27:03
JassBug.jpg
回复

使用道具 举报

 楼主| 发表于 2007-12-14 12:47:27 | 显示全部楼层
“共用空间论”目前只是我为了方便理解该现象而提出的假设~~并没有实际地证明过~~

既然目前还没有遇到可证伪的证据~~就可以一直这样子理解了~~

当然也就不必太拘泥于此~~
回复

使用道具 举报

匿名
匿名  发表于 2007-12-14 12:48:30
(发现引导者又多了一个.)
回复

使用道具 举报

 楼主| 发表于 2007-12-14 12:59:49 | 显示全部楼层
引用第6楼eff于2007-12-14 11:18发表的  :
很有趣的BUG。使用有一定风险


到目前为止~~这东西的风险也没return bug大~~

导致string crash的原因是string一共也就被分配了这点handle配额~~数字太大当然就越界了~~

实际上你用return bug来take real return string结果一样会crash~~

其实我们现在用return bug实现的H2S函数其实是H2I然后用官方函数I2S()来把I变成字符本身~~而不是作为string的handle~~
回复

使用道具 举报

 楼主| 发表于 2007-12-14 13:00:28 | 显示全部楼层
直接将函数参数来覆盖全局变量~~可以少写一句声明的说~~
回复

使用道具 举报

 楼主| 发表于 2007-12-14 13:13:20 | 显示全部楼层
经过测试呢~~试图声明一个被覆盖数组是不允许的~~直接就无法通过we保存~~

这个也可以算作是空间唯一性的佐证罢?~~只能开辟一片空间~~无法开辟多片空间~~
回复

使用道具 举报

 楼主| 发表于 2007-12-14 14:04:03 | 显示全部楼层
这里有一个特别的实例~~

[jass]
function U2I takes integer udg_i, unit udg_u returns integer
return udg_i
endfunction
[/jass]

这是实现Union bug最简短的函数式写法~~

虽然我不推荐把union bug的类型转换写成函数~~因为不通过函数直接转换才是它的优点~~而写成函数的话就不见得比return快~~虽然2者都只执行一步~~但是这里毕竟要多个参数~~

不过我把这个函数帖在这里是为了更方便大家理解被覆盖变量作为参数也能使用~~

这个函数的调用方法是
[jass]
set i =U2I(null,u)
[/jass]
第一个参数其实填什么数字都可以~~因为它本来就是用来被覆盖的~~u则是需要转换的单位~~


如果你已经彻底理解了上文~~就很容易理解这函数究竟是如何产生效果的?~~注意两个参数是不能对调声明顺序的~~因为函数被赋值的顺序也和声明顺序有关~~
回复

使用道具 举报

发表于 2007-12-15 09:08:32 | 显示全部楼层
这些东西都是BUG,BUG就没必要出得没BUG了~~哈哈
回复

使用道具 举报

发表于 2007-12-15 09:41:27 | 显示全部楼层
555~ 又一个强大的bug

这样子使用应该不会让代码显得非常的火星...
[codes=jass]
globals
integer union_int
handle union_handle
endglobals

//! define USE_UNION_INT local integer union_int
//! define USE_UNION_HANDLE local handle union_handle

//! define SetUnionInt(x) set union_int=x
//! define SetUnionHandle(x) set union_handle=x
//! define GetUnionInt() union_int
//! define GetUnionHandle() union_handle
[/codes]

当然这是NewGen或WEHelper上的做法,代码A格式实际上会被转化为B:
[codes=jass]
A:
function a takes nothing returns integer
USE_UNION_INT
USE_UNION_HANDLE

SetUnionHandle(CreateTimer())
return GetUnionInt()
endfunction

B:
function a takes nothing returns integer
local integer union_int
local handle union_handle

set union_handle=CreateTimer()
return union_int
endfunction
[/codes]
回复

使用道具 举报

发表于 2007-12-15 11:56:46 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
回复

使用道具 举报

发表于 2007-12-16 16:52:46 | 显示全部楼层
define功能貌似是WEH独有的....
猪说过~
回复

使用道具 举报

 楼主| 发表于 2008-2-28 10:35:26 | 显示全部楼层
为了写handle释放教程~~今天测试了一下Union Bug的handle 泄露~~

发现只要将其中任何一个变量设null或者设0就可以了~~
以下2个法子都行~~

[trigger]
HandleRelease
    Events
        Player - Player 1 (Red) skips a cinematic sequence
    Conditions
    Actions
        Custom script:   local integer udg_Re
        Custom script:   local trigger udg_t1
        Custom script:   local trigger udg_t2=CreateTrigger()
        Trigger - Destroy t1
        Debug - Debug (String(Re))
        Set t2 = No trigger
[/trigger]

[trigger]
HandleRelease
    Events
        Player - Player 1 (Red) skips a cinematic sequence
    Conditions
    Actions
        Custom script:   local integer udg_Re
        Custom script:   local trigger udg_t1
        Custom script:   local trigger udg_t2=CreateTrigger()
        Trigger - Destroy t1
        Debug - Debug (String(Re))
        Set Re = 0
[/trigger]
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 点一下

本版积分规则

Archiver|移动端|小黑屋|地精研究院

GMT+8, 2024-11-21 21:43 , Processed in 0.162260 second(s), 21 queries .

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表