在 .data 中对数组进行索引会导致地址远高于数组

问题描述 投票:0回答:1

我的作业是在 MASM 中更改数组内的值,交换对。

array[0] = array[1]
array[1] = array[0]
array[2] = array[3]
array[3] = array[2]

等等......

我已经放弃使用

var1
var2
变量,因为我似乎无法让它们工作。我有

.data
; add data here
array01 DWORD 100, 200, 300, 400, 500, 600
var1 DWORD ?
var2 DWORD ?

.code
main PROC
    mov edi, OFFSET array01         ; 1: EDI = address of array01
    mov ecx, LENGTHOF array01       ; 2: initialize loop counter
    mov eax, 0                      ; 3: sum =0

L1:                                 ; 4: Mark beginning of loop
    mov eax, [edi]                  ; 5: Get var1
    add edi, TYPE array01           ; 6: increment index
    mov ebx, [edi]                  ; 7: Get var2
    sub edi, TYPE array01           ; 8: decrement to get index to point to the right location

    mov array01[edi], ebx           ; 9: Change array01[i] to var1
    add edi, TYPE array01           ; 10: increment index
    mov array01[edi], eax           ; 11: Change array01[i] to var1
    loop L1                         ; 12: repeats until ecx is 0

    INVOKE ExitProcess, 0
main ENDP
End main

我的问题出在指令 9 上。它给了我一个错误

Assignment_03.exe 中 0x007C1032 处出现未处理的异常:0xC0000005:写入位置 0x00F88004 时发生访问冲突。

从调试器中,我的 EIP =

007C1032
但我的 EDI =
007C4000
我的
&array01
位于
0x007C4000

早期的跑步有

Exception thrown at 0x00171029 in Assignment_03.exe: 0xC0000005: Access violation writing location 0x002E8000.

我知道它告诉我正在访问一些不允许的内容,但我不知道如何修复它。


我已经尝试过这个: https://termspar.wordpress.com/2019/10/12/x86- assembly-swap-array-elements/

mov [numList+ebx],dh

但我仍然遇到同样的错误。


我做错了什么?您能解释一下解决方案吗?需要明确的是,我不想要我的家庭作业的答案。我需要更改数组中的值的帮助。

mov dword ptr [array01], 700
可以更改第一个元素,但使用 EDI 建立索引不起作用。

arrays pointers assembly x86 masm
1个回答
0
投票

使用

mov [edi], ebx

mov array01[edi], ebx
是将EDI中的指针添加到数组的地址上。如果 EDI 是一个小数字,即距数组开头的字节偏移量,那么这是正确的。但在本例中并非如此,您使用 EDI 作为指针本身,而
mov edi, OFFSET array01
在循环之外。

该地址计算类似于 C

int *ptr = array01;
array01[ (int)ptr ] = tmp2;
,但不按类型宽度进行缩放。在聊天讨论中,您使用调试器发现
array01
的实际地址是
0x007C4000
,但错误地址报告为写入
0x00F88004
。这是更仔细地查看代码到底在做什么的线索。

0x007C4000 * 2 = 0xF88000
。我不知道多余的
...4
是从哪里来的。第一个存储位于
sub edi, 4
之后,因此应该使用 EDI 恢复其原始值。 (内存保护使用 4K 页粒度,因此如果 0xF88004 出现故障,0xF88000 应该在第一次迭代中出现故障,就像调试器显示实际发生的情况一样。)


那么为什么它在存储上出错而不是在加载上出错?您对加载使用了正确的寻址模式,

[edi]
,只是取消引用指针,而不是向其添加另一个地址!

起初我认为该地址处有只读内存很奇怪,但事实并非如此,第一次访问是通过存储进行的。


顺便说一句,从

[edi]
[edi+4]
加载和存储会简单得多,因此您的循环只需要一个
add edi, 8
即可前进到下一对。 (目前,每次迭代您将前进 8 次并后退 4 次,净前进 1 个 4 字节元素。)

您必须调整循环计数器以避免超出范围;使用指向数组末尾的指针和

cmp / 
jb L1
against that, instead of calculating an iteration count for the slow
loop
instruction.  You need to stop even if there's 0 or 1 elements left, like the bottom of a
do{... ; 可能会更容易ptr+=8;}while(ptr < endp-4);` where endp is a label at the end of the array.


顺便说一句,有趣的方法是使用 SIMD 加载 4x 32 位元素(2 对),对它们进行洗牌,然后将它们存储回来。由于本例中问题大小恰好是 2 对,因此我们可以放弃循环:P

   movdqu xmm0, xmmword ptr [array01]   ; 16-byte load
   pshufd xmm0, xmm0, 0b10_11_00_01  ; 2-bit indices for 4x 4-byte elements, packed into an 8-bit immediate.
              ; NASM accepts that syntax for binary literals, MASM probably doesn't
              ; _mm_shuffle_epi32(vec, _MM_SHUFFLE(2,3, 0,1))
   movdqu xmmword ptr [array01], xmm0   ; 16-byte store

您可能需要告诉 MASM 允许 SSE2 向量指令。如今,即使在 32 位代码中,C 编译器也默认使用它们。但显然使用 16 字节 shuffle 不会教你编写循环。

另一种有趣的方法是编写 64 位代码并执行诸如

ror qword ptr [rdi], 32
之类的操作,将 64 位旋转 32,以交换 64 位内存块的一半。

© www.soinside.com 2019 - 2024. All rights reserved.