我正在调试用VB6开发的旧代码,发现有些奇怪的东西。可以使用以下简单代码进行演示:
Private Sub Command1_Click()
Dim a As Integer
Dim b As Integer
Dim c As Integer
a = 0
Text1.Text = a - 1
Text2.Text = CStr((a - 1) Mod 4)
b = 0
b = b - 1
Text3.Text = b
Text4.Text = CStr(b Mod 4)
c = 0 - 1
Text5.Text = c
Text6.Text = CStr(c Mod 4)
End Sub
Run Code Online (Sandbox Code Playgroud)
表单如下所示:
您会认为单击按钮后,Text2,Text4和Text6应该显示相同的内容,即-1。我按F5键在IDE中运行它时就是这种情况。
这是我在IDE中运行它时的样子:
当我从IDE中制作一个exe并运行该exe本身时,发生了一件奇怪的事情。Text1和Text6显示-1,而Text4显示3。
这是我在IDE外部运行exe时的样子:
仅当第二个操作数为2的幂时,才会发生这种情况。当我将4更改为5时,所有文本框都显示为-1。
我在2台Windows 10机器上对此进行了测试,并得到了相同的结果。
我知道VB6很旧,现在仍然没有多少人可以测试它。如果有人可以帮助我理解这一点,我将深表感谢。
谢谢。
这似乎是一个编译器错误。编译器无法识别该值在Integer(16 位值)的情况下是否有符号,但在Long.
执行模 4 的代码在每种情况下几乎相同,并且遵循模 2 幂的优化模式:
长的 (b Mod 4&):
or eax, 0xFFFFFFFF # eax = 0xFFFFFFFF (which is -1)
and eax, 0x80000003 # eax = 0x80000003 The modulo op, note it's signed because of the 8
jns other_code # Skip the next three lines if the result is non-negative (it isn't here)
dec eax # eax = 0x80000002
or eax, 0xFFFFFFFC # eax = 0xFFFFFFFE
inc eax # eax = 0xFFFFFFFF (which is -1)
other_code:
Run Code Online (Sandbox Code Playgroud)
最后eax包含0xFFFFFFFF,也就是-1,传递给显示。
整数 (b Mod 4):
or eax, 0xFFFFFFFF # eax = 0xFFFFFFFF, ax = 0xFFFF (which is -1 in both cases)
and ax, 0x3 # eax = 0xFFFF0003, ax = 0x0003. Should have been "and ax, 0x8003!"
jns other_code # Skip the next three lines if the result is non-negative (it incorrectly is)
dec ax # Skipped
or ax, 0xFFFC # Skipped
inc ax # Skipped
other_code:
Run Code Online (Sandbox Code Playgroud)
最后eax包含 0xFFFF0003,然后传递给函数,__vbaStrI2该函数显然会忽略两个高字节并仅使用0003.
如果and ax, 0x8003使用 代替and ax, 0x3,则跳过的行将触发并将 转换0xFFFF0003为0xFFFFFFFF,即 -1。
禁用优化后,按位模数学将替换为直接除法:
sub ax, 0x1 # b = b - 1
mov cx, 0x4 # Prepare division by 4
idiv cx # Integer division
Run Code Online (Sandbox Code Playgroud)
至于为什么a和 的情况c按预期工作,这是因为编译器-1 Mod 4在编译阶段进行计算并将结果硬编码到可执行文件中:
push 0xFFFFFFFF # Pass hardcoded -1 for display
other_code:
Run Code Online (Sandbox Code Playgroud)
从技术上讲,没有什么可以阻止它在 的情况下做同样的事情b,因为它也可以证明被除的值是-1。我无法确定为什么它不这样做 - 也许它太早停止了静态分析一步,或者也许将其写回-1内存地址的结果代码b在它看来效率较低。