You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

汇编语言数组扩容异常:无法输入超过5个元素

问题描述

我尝试通过数组容量翻倍的方式添加更多元素,程序编译运行都没报错,但输入5个元素后就自动进入统计输出环节,没法继续输入。不确定数组到底有没有扩容,需求是数组填满时自动翻倍,直到按下Ctrl+D再输出统计信息。尝试修改rcx来继续循环输入却触发了段错误,相关代码如下:

%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 ; 调试用输出
   newline:     db 10,0                  ; 换行符
   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                     ; 数组指针
   intInput:  resd 1

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

   ;; 为数组分配内存
   ;; calloc参数:元素数量、元素大小,返回零初始化内存的指针
   mov   rdi, ARRAY_SIZE
   mov   rsi, ELEMENT_SIZE
   call  calloc
   mov   [arrayPtr], rax
   
   mov   rdi, [arrayPtr]        ; 用RDI和stosd写入数组
   mov   rcx, ARRAY_SIZE
   mov   r15, 0                 ; R15统计已输入元素数量
   cld
inputLoop:
   push  rcx                    ; 保存RCX和RDI,避免被printf/scanf破坏
   push  rdi
   mov   rdi, inputPrompt
   call  printf

   mov   rdi, intFormat
   mov   rsi, intInput
   call  scanf

   cmp   eax, EOF               ; 检查scanf是否返回EOF(未读取到内容)
   je    inputDone
  
   ; 检查数组是否已满,满则扩容
   cmp   r15, ARRAY_SIZE
   jl    inputNotFull
   ; 翻倍数组容量并复制元素到新数组
   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
  
   ; 继续处理输入
inputNotFull:
   inc   r15                    ; 计数+1并存储元素
   xor   rax, rax               ; 清空RAX以便stosd写入内存
   mov   eax, [intInput]
   pop   rdi                    ; 恢复RDI用于stosd
   stosd
   pop   rcx                    ; 恢复RCX用于循环
   dec   rcx
   jnz   inputLoop
   
inputDone:                      ; 准备打印
   mov   rdi, newline
   call  printf
   mov   rsi, [arrayPtr]        ; 用RSI和lodsd读取数组
   mov   rcx, r15               ; 实际元素数量存入RCX
   mov   rbx, 0
   cld

printLoop:
   xor   rax, rax
   xor   rdx, rdx
   lodsd
   push  rcx                    ; 保存RCX和RSI避免被printf破坏
   push  rsi
   mov   rdi, output
   mov   rsi, rbx

   movsx rdx, eax
   call  printf
   inc   rbx

   pop   rsi                    ; 恢复RCX和RSI
   pop   rcx
   loop  printLoop
   je    printStats
   
printStats:
   mov   rdi, newline
   call  printf
   mov   rdi, numElements
   mov   rsi, r15
   call  printf
   
   mov   rax, 0         ; 初始化总和为0
   mov   rsi, [arrayPtr] ; 获取数组地址
   mov   rcx, r15        ; 实际元素数量存入RCX
   cld
   
   xor   rdx, rdx       ; 清空RDX用于第一次加法
   
   mov   rax, 0           ; 初始化总和为0
   mov   rsi, [arrayPtr]  ; 获取数组地址
   mov   rcx, r15         ; 实际元素数量存入RCX
   cld
sumLoop:
   lodsd                  ; 将数组下一个元素加载到EAX
   add   rax, r8          ; 累加到总和
   mov   r8, rax          ; 保存更新后的总和到R8
   loop  sumLoop
   mov   rdi, outputSum
   mov   rsi, r8
   call  printf
   
 ; 查找数组最小值
   mov   rax, [arrayPtr]     ; 将数组基地址加载到RAX
   mov   ebx, [rax]          ; 将第一个元素加载到EBX
   mov   rcx, r15            ; 循环计数器设为元素数量
   dec   rcx                 ; 已加载第一个元素,计数器减1
   mov   rsi, [rax]          ; 初始化RSI为第一个元素
loopStart:
   add   rax, ELEMENT_SIZE   ; 指针移动到下一个元素
   cmp   [rax], ebx          ; 比较当前元素与最小值
   jge   loopEnd             ; 大于等于最小值则跳过更新
   mov   ebx, [rax]          ; 否则更新最小值
   mov   rsi, rax            ; 保存最小值的地址到RSI
loopEnd:
   loop  loopStart           ; 循环处理剩余元素
   mov   rdi, minValue
   mov   rsi, rbx
   call  printf

; 查找数组最大值
   mov   rax, [arrayPtr]     ; 将数组基地址加载到RAX
   mov   ebx, [rax]          ; 将第一个元素加载到EBX
   mov   rcx, r15            ; 循环计数器设为元素数量
   dec   rcx                 ; 已加载第一个元素,计数器减1
   mov   rsi, [rax]          ; 初始化RSI为第一个元素
loopStartd:
   add   rax, ELEMENT_SIZE   ; 指针移动到下一个元素
   cmp   [rax], ebx          ; 比较当前元素与最大值
   jle   loopEndd             ; 小于等于最大值则跳过更新
   mov   ebx, [rax]          ; 否则更新最大值
   mov   rsi, rax            ; 保存最大值的地址到RSI
loopEndd:
   loop  loopStartd
   mov   rdi, maxValue
   mov   rsi, rbx
   call  printf

   mov   rax, 0
   leave 
   ret
问题根源与修复方案

1. 错误修改预编译常量

%define ARRAY_SIZE 5是预编译阶段定义的常量,直接执行mov [ARRAY_SIZE], eax会试图往只读的常量地址写入数据,属于非法操作,会引发内存错误。

  • 修复:在.bss段新增内存变量存储当前容量:
    segment .bss
       arrayPtr:  resq 1
       intInput:  resd 1
       currentCap: resd 1  ; 存储当前数组容量,替代原ARRAY_SIZE常量
    
  • 初始化时设置初始容量:
    mov dword [currentCap], ARRAY_SIZE
    

2. realloc调用参数顺序错误

realloc的第一个参数是原内存指针,第二个参数是新的总字节数,原代码参数顺序完全颠倒,且手动执行rep movsd属于多余操作(realloc会自动完成内存复制)。

  • 修复后的扩容代码:
    ; 扩容:容量翻倍
    mov rdi, [arrayPtr]       ; 第一个参数:原数组指针
    mov eax, [currentCap]
    imul eax, 2               ; 容量翻倍
    mov esi, eax
    imul esi, ELEMENT_SIZE    ; 计算新的总字节数
    call realloc
    test rax, rax             ; 检查内存分配是否成功
    jz inputDone              ; 分配失败则直接退出
    mov [arrayPtr], rax
    mov [currentCap], eax     ; 更新当前容量
    ; 更新rdi到新数组的下一个可写入位置
    mov rdi, [arrayPtr]
    mov rax, r15
    imul rax, ELEMENT_SIZE
    add rdi, rax
    

3. 输入循环终止逻辑错误

原代码依赖rcx作为剩余输入次数计数器,扩容后未更新rcx,导致输入5次后rcx变为0,触发jnz inputLoop退出。

  • 修复:移除依赖rcx的终止逻辑,改为无条件回到输入循环,仅在收到EOF时退出:
    inputNotFull:
       inc r15
       xor rax, rax
       mov eax, [intInput]
       pop rdi
       stosd
       pop rcx
       jmp inputLoop  ; 回到输入循环,直到收到EOF
    

4. 求和逻辑未初始化变量

原求和代码中r8未初始化,会导致随机值参与计算,结果错误。

  • 修复:在求和前初始化r8为0:
    xor r8, r8  ; 初始化总和为0
    sumLoop:
       lodsd
       add r8, rax  ; 将当前元素加到总和
       loop sumLoop
    

内容的提问来源于stack exchange,提问作者user21541086

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 15:09:59