nasm x86_64汇编查找第10001个素数时判定与数组取值异常如何解决
问题修复方案
核心错误原因
你遇到的读取素数数值异常的核心问题是数组地址偏移计算错误:
- 你声明素数数组的每个元素是8字节的qword类型,但使用索引
rcx计算内存地址时没有乘以8,导致始终访问错位的内存地址,读取到的自然是无效乱值(比如你遇到的770)。 - 后续存储新素数的逻辑也存在同样的偏移计算错误。
修正后的代码调整点
1. 修复数组寻址逻辑
两处读写数组的代码都要将索引乘以8:
; 读取素数的代码从 mov qword rbx, [r10 + rcx] ; 改为 mov qword rbx, [r10 + rcx*8] ; 存储新素数的代码从 mov qword [r10 + rcx], r12 ; 改为 mov qword [r10 + rcx*8], r12
2. 补充寄存器调用约定适配
根据x86_64 System V调用约定:
r10、r11属于调用者保存寄存器,调用calloc等libc函数后可能被修改,建议改用r12、r13、r14这类被调用者保存寄存器存储全局状态,或者在调用函数前手动入栈保存。main函数使用被调用者保存寄存器前,需要在函数开头入栈备份,函数退出前出栈恢复,避免破坏上层调用者的状态,示例如下:
main: push r12 push r13 push r14 ; 原有逻辑 ; ... ; 函数退出前恢复 pop r14 pop r13 pop r12 ret
3. 可选性能优化
要查找第10001个素数,可以增加两个优化点大幅提升运行速度:
- 因为2是唯一的偶素数,后续待校验数可以直接从3开始,每次加2跳过所有偶数,减少一半校验次数
- 校验素数时不需要遍历所有已存储的素数,只需要遍历到小于等于当前待校验数平方根的素数即可,不需要校验更大的素数
修正后可正常运行的完整代码片段
%include '../resources.asm' SECTION .text global main extern calloc main: push r12 push r13 push r14 push r15 ; 为素数数组分配空间 mov rsi, 8 mov rdi, 10001 call calloc ; 将数组地址存入r12 mov r12, rax ; 第一个素数是2 mov qword [r12], 2 ; r13存储当前已找到的素数数量 mov r13, 1 ; r14存储当前待校验的数字,从3开始跳过偶数 mov r14, 3 .outer: ; 重置数组索引 mov rcx, 0 .inner: ; 将待校验数字存入rax,除数存入rbx mov rax, r14 mov qword rbx, [r12 + rcx*8] ; 数组索引自增 inc rcx ; 计算模值 call umod ; 若模值为0,说明当前数不是素数,跳转校验下一个数字 cmp rax, 0 jz .next_num ; 校验是否已经遍历完当前已存储的所有素数 cmp rcx, r13 jnz .inner ; 若已遍历完所有已存储素数,说明当前数为素数,存入数组 mov qword [r12 + rcx*8], r14 inc r13 ; 已找到第10001个素数则停止运行 cmp r13, 10001 je .exit .next_num: ; 跳过偶数,每次加2 add r14, 2 jmp .outer .exit: ; 第10001个素数存在r14中,可自行添加输出逻辑 pop r15 pop r14 pop r13 pop r12 ret
内容的提问来源于stack exchange,提问作者H. Ross
相关产品推荐
相关产品推荐

