NASM中使用scanf读取浮点数组出现段错误,如何修复?
嘿,帮你搞定汇编代码的段错误问题!
首先看你给出的代码片段,能发现几个明显的坑,这些就是导致段错误的元凶,我一个个给你拆解,再给你修复后的完整代码:
1. 数组空间没分配对
你代码里的array res...明显没写完对吧?静态数组在.bss段得明确说要占多少空间,但你要存n个double(每个8字节),静态分配没法直接用变量n,所以最好用动态分配内存(调用malloc),这样能根据输入的n来分配刚好够的空间,不会浪费也不会不够。
2. scanf读取double的姿势错了
在x86-64的System V调用约定里,scanf的%lf要求传的是指向double的指针,而且得放在通用寄存器里,不是浮点寄存器。很多新手会直接传值,或者地址算错,这肯定会触发内存访问错误。
3. 循环里的数组索引算错了
每个double占8字节,循环访问的时候得把地址每次加8才能跳到下一个元素,如果偏移量算错,比如加了4,那就会访问到中间的字节,直接越界触发段错误。
修复后的完整代码(带注释)
%macro exit 0 mov rax,60 mov rdi,0 syscall %endmacro extern printf extern scanf extern malloc ; 引入malloc做动态内存分配 extern free ; 用完内存记得释放,好习惯 ;------------------------------------------- section .data nfmt db "%d",0 formatsf db "%lf",0 formatpf db "%.2lf",10,0 ; 格式化输出保留两位小数,看着更清楚 err_msg db "内存分配失败啦!",10,0 ;------------------------------------------- section .bss n resq 1 array resq 1 ; 这里存动态分配的数组指针,不是数组本身 ;------------------------------------------- section .text global main main: ; 先读取n的值 mov rdi, nfmt mov rsi, n mov rax, 0 call scanf ; 计算需要的内存大小:n * 8字节(每个double占8字节) mov rdi, [n] imul rdi, 8 call malloc cmp rax, 0 je alloc_fail ; 如果malloc返回0,说明分配失败,跳去处理错误 mov [array], rax ; 把分配到的数组指针存起来 ; 循环读取n个double值 mov rcx, [n] ; 用rcx当循环计数器 mov rbx, [array] ; rbx存当前数组元素的地址 read_loop: mov rdi, formatsf mov rsi, rbx ; 传当前元素的地址给scanf mov rax, 0 call scanf add rbx, 8 ; 地址加8,跳到下一个double元素 dec rcx jnz read_loop ; 计数器没到0就继续循环 ; 循环打印数组里的内容 mov rcx, [n] mov rbx, [array] print_loop: mov rdi, formatpf movsd xmm0, [rbx] ; 把double值加载到XMM0寄存器(printf的浮点参数用XMM) mov rax, 1 ; 告诉printf:我有1个浮点参数在XMM寄存器里,这步很重要! call printf add rbx, 8 dec rcx jnz print_loop ; 释放动态分配的内存,避免内存泄漏 mov rdi, [array] call free exit alloc_fail: ; 打印内存分配失败的提示 mov rdi, err_msg mov rax, 0 call printf exit
关键改动说明
- 动态内存分配:用
malloc根据输入的n来分配刚好的空间,还加了分配失败的处理,避免程序直接崩溃。 scanf参数修正:传的是数组元素的地址,完全符合%lf的要求,不会再乱访问内存。printf的浮点参数处理:把double值加载到XMM0,还要设置rax为浮点参数的数量,这是x86-64调用约定的硬性要求,之前没做的话不仅输出错,还可能崩溃。- 循环地址偏移:每次加8,精准定位到下一个double元素,不会越界。
内容的提问来源于stack exchange,提问作者welkin
相关产品推荐
相关产品推荐

