在 Assembly 中将数组大小加倍的问题

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

尝试将数组加倍以增加数组以添加更多元素。 没有编译或运行时错误,但它不会让你在 5 之后输入更多元素 我不确定数组大小是否根本没有加倍或者发生了什么,因为你可以插入 5 个数字然后输入将去完成并打印统计数据。每次用户填充数组时,我都需要它使数组加倍,直到他们按下(ctrl d),然后它才会输出统计信息。


   %define ARRAY_SIZE  5
   %define ELEMENT_SIZE 4
   %define EOF -1
   
segment .data
   inputPrompt: db "Enter int value (ctrl-d to stop): ",0
   intFormat:   db "%d",0
   output:      db "Array[%d] = %d",10,0
   dbout:       db 10, 10,"[%d]",10,10,0 ; This is just for debugging output
   newline:     db 10,0                  ; For whenever we need a newline
   numElements: db "Number of elements: %d",10,0
   outputSum:   db "sum of elements: %d",10,0
   minValue:    db "Min value: %d",10,0
   maxValue:    db "Max value: %d",10,0

segment .bss
   arrayPtr:  resq 1                     ; pointer to our array
   intInput:  resd 1

segment .text
   global asm_main
   extern printf, scanf, calloc, realloc
   
asm_main:
   enter 0,0

   ;; Get memory for our array
   ;; Give calloc() number of element and element size
   ;; and calloc returns a pointer to zerioized memory
   mov   rdi, ARRAY_SIZE
   mov   rsi, ELEMENT_SIZE
   call  calloc
   mov   [arrayPtr], rax
   
   mov   rdi, [arrayPtr]        ; Will use RDI and stosd to write the array
   mov   rcx, ARRAY_SIZE
   mov   r15, 0                 ; R15 counts how many elements in our array
   cld
inputLoop:
   push  rcx                    ; Save RCX and RDI across printf/scanf calls
   push  rdi
   mov   rdi, inputPrompt
   call  printf

   mov   rdi, intFormat
   mov   rsi, intInput
   call  scanf

   cmp   eax, EOF               ; Did scanf() return -1 (didn't read anything?)
   je    inputDone
  
   ; check if the array is full, if so, double its size
   cmp   r15, ARRAY_SIZE
   jl    inputNotFull
   ; double the array size and copy the elements to the new array
   mov   rsi, [arrayPtr]
   mov   rdi, ARRAY_SIZE*2*ELEMENT_SIZE
   call  realloc
   mov   [arrayPtr], rax
   mov   rcx, [ARRAY_SIZE]
   rep   movsd
   mov   eax, ARRAY_SIZE
   imul  eax, 2
   mov  [ARRAY_SIZE], eax
  
   ; continue with the input
inputNotFull:
   inc   r15                    ; O/w count another element & store it
   xor   rax, rax               ; Clear out RAX for stosd to write array to mem
   mov   eax, [intInput]
   pop   rdi                    ; Restore RDI for stosd
   stosd
   pop   rcx                    ; Restore RCS for loop instruction
   dec   rcx
   jnz   inputLoop
   
inputDone:                      ; Let's get ready to print
   mov   rdi, newline
   call  printf
   mov   rsi, [arrayPtr]        ; Will use RSI and lodsd to read the array
   mov   rcx, r15               ; Store actual array size to RCX
   mov   rbx, 0
   cld

printLoop:
   xor   rax, rax
   xor   rdx, rdx
   lodsd
   push  rcx                    ; Save RCX and RSI across printf call
   push  rsi
   mov   rdi, output
   mov   rsi, rbx

   movsx rdx, eax
   call  printf
   inc   rbx

   pop   rsi                    ; Restore RCX and RSI
   pop   rcx
   loop  printLoop
   je    printStats
   
printStats:
   mov   rdi, newline
   call  printf
   mov   rdi, numElements
   mov   rsi, r15
   call  printf
   
   mov   rax, 0         ; Initialize the sum to zero
   mov   rsi, [arrayPtr] ; Get the address of the array
   mov   rcx, r15        ; Store actual array size to RCX
   cld
   
   xor   rdx, rdx       ; Clear out RDX for the first add instruction
   
   mov   rax, 0           ; Initialize the sum to zero
   mov   rsi, [arrayPtr]  ; Get the address of the array
   mov   rcx, r15         ; Store actual array size to RCX
   cld
sumLoop:
   lodsd                  ; Load the next element of the array into EAX
   add   rax, r8          ; Add the current sum to EAX
   mov   r8, rax          ; Store the updated sum in R8
   loop  sumLoop
   mov   rdi, outputSum
   mov   rsi, r8
   call  printf
   
 ; Find the minimum value in the array
   mov   rax, [arrayPtr]     ; Load the base address of the array into RAX
   mov   ebx, [rax]          ; Load the first element of the array into EBX
   mov   rcx, r15            ; Loop counter will be the number of elements
   dec   rcx                 ; Decrement RCX since we've already loaded the first element
   mov   rsi, [rax]          ; Initialize RSI to the first element
loopStart:
   add   rax, ELEMENT_SIZE   ; Move the pointer to the next element
   cmp   [rax], ebx          ; Compare the value at the current pointer to the minimum value
   jge   loopEnd             ; If the value is greater than or equal to the minimum, skip to the end of the loop
   mov   ebx, [rax]          ; Otherwise, update the minimum value
   mov   rsi, rax            ; and store the address of the minimum value in RSI
loopEnd:
   loop  loopStart           ; Repeat for the remaining elements of the array
   mov   rdi, minValue
   mov   rsi, rbx
   call  printf

; Find the maximum value in the array
   mov   rax, [arrayPtr]     ; Load the base address of the array into RAX
   mov   ebx, [rax]          ; Load the first element of the array into EBX
   mov   rcx, r15            ; Loop counter will be the number of elements
   dec   rcx                 ; Decrement RCX since we've already loaded the first element
   mov   rsi, [rax]          ; Initialize RSI to the first element
loopStartd:
   add   rax, ELEMENT_SIZE   ; Move the pointer to the next element
   cmp   [rax], ebx          ; Compare the value at the current pointer to the maximum value
   jle   loopEndd             ; If the value is less than or equal to the maximum, skip to the end of the loop
   mov   ebx, [rax]          ; O/w, update the maximum value to the current value
   mov   rsi, rax            ; Store the address of the current maximum value
loopEndd:
   loop  loopStartd
   mov   rdi, maxValue
   mov   rsi, rbx
   call  printf

   mov   rax, 0
   leave 
   ret

我尝试增加 rcx,因为这决定了它是否会跳回 inputLoop:以获得更多输入,但随后会出现分段错误。

arrays assembly memory-management x86-64 nasm
1个回答
0
投票

5

后不让你输入更多元素

这仅仅是因为 inputLoop 被告知进行 5 次迭代,之后它在 inputDone 中失败。
本质上,你需要的是一个无条件跳回到 inputLoop 顶部的 endless loop。当然,在 scanf 返回 EOF 时,总是会有已经可用的提前退出。

我试图增加 rcx ...但是它给出了一个分段错误。

上述问题实际上是一件幸事,因为正如您所报告的那样,增加迭代次数会触发“分段错误”。这是因为在调用 realloc 时没有遵循规则。地址进入 RDI,新的总大小进入 RSI;你把它弄反了!此外,如果 realloc 必须在物理上重新定位分配(大多数情况下不需要),那么它会将内容一起移动。你不需要从头做起,当然也不需要使用错误的寄存器。

我对你这部分节目的建议

我建立这个前提是数组中元素数量的动态适应应该遵循序列 5 -> 10 -> 20 -> 40 ...并且除非绝对必要,否则不执行任何更改(例如,在数组大小仍为 5 时接收到第 6 个输入,或者在数组大小仍为 10 时接收到第 11 个输入,依此类推):

   mov   rdi, ARRAY_SIZE
   mov   rsi, ELEMENT_SIZE
   call  calloc                 ; -> RAX
   mov   [arrayPtr], rax
   add   rax, ARRAY_SIZE * ELEMENT_SIZE
   mov   [arrayEnd], rax

   ...

   mov   rdi, [arrayPtr]
inputLoop:
   push  rdi                    ; (1)
   mov   rdi, inputPrompt
   call  printf
   mov   rdi, intFormat
   mov   rsi, intInput
   call  scanf
   pop   rdi                    ; (1)
   cmp   eax, EOF               ; Did scanf() return -1?
   je    inputDone

   cmp   rdi, [arrayEnd]
   jb    inputNotFull

   mov   rdi, [arrayPtr]
   mov   rsi, [arrayEnd]
   sub   rsi, rdi
   shl   rsi, 1                 ; Double current size
   push  rsi                    ; (2)
   call  realloc                ; -> RAX
   pop   rsi                    ; (2)
   mov   [arrayPtr], rax
   add   rax, rsi
   mov   [arrayEnd], rax
   shr   rsi, 1
   sub   rax, rsi
   mov   rdi, rax               ; Continue at midpoint

inputNotFull:
   mov   eax, [intInput]
   stosd
   jmp   inputLoop

inputDone:

请注意,我引入了一个新变量 arrayEnd,它包含一个指向数组后面的指针,类似于 arrayPtr 包含一个指向数组开头的指针。
提示:你不应该检查 callocrealloc 是否成功返回(检查 RAX)吗?

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