VB6在计算负数Mod为2的幂的数字时的奇怪行为

eli*_*lin 6 vb6

我正在调试用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很旧,现在仍然没有多少人可以测试它。如果有人可以帮助我理解这一点,我将深表感谢。

谢谢。

GSe*_*erg 2

这似乎是一个编译器错误。编译器无法识别该值在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,则跳过的行将触发并将 转换0xFFFF00030xFFFFFFFF,即 -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在它看来效率较低。