assembly 在程序集中将数组大小加倍时出现问题

xqnpmsa8  于 2023-04-06  发布在  其他
关注(0)|答案(1)|浏览(140)

尝试将数组加倍以增加数组以添加更多元素。没有编译或运行时错误,但它不允许您在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:以获得更多的输入,但随后它给出分段错误。

kninwzqo

kninwzqo1#

它不允许你在5之后输入更多的元素
这仅仅是因为 inputLoop 被告知进行5次迭代,之后它在 inputDone 中福尔斯。
本质上,你需要的是一个无限循环,它无条件地跳回到 inputLoop 的顶部。当然,在 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)?

相关问题