汇编语言数组扩容异常:无法输入超过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
相关产品推荐
相关产品推荐

