汇编中调用scanf读取浮点数触发段错误,未在XMM寄存器返回浮点数
解决汇编中第三次调用scanf触发段错误的问题
首先,咱们先拆解问题核心:你要写汇编程序读取特定格式的输入,第三次调用scanf时崩了——这在汇编里大多是调用约定不遵守、参数/变量匹配错误,或者栈对齐问题导致的。结合你的输入格式和数据结构需求,我给你一套可运行的示例代码,同时把踩坑点讲清楚。
先明确关键前提(以x86_64 Linux为例,最常见的环境)
汇编调用C标准库的scanf,必须严格遵守System V AMD64 ABI:
- 前6个参数依次存在
rdi、rsi、rdx、rcx、r8、r9寄存器里 - 超过6个的参数压栈,且调用前栈必须是16字节对齐
scanf是可变参数函数,调用前要把eax设为0(因为咱们用的是浮点格式,但这里都是通过内存地址传递,不需要用XMM寄存器传参)- 格式字符串必须以NULL(
0)结尾,否则scanf会读取越界
完整示例代码
1. 数据段(格式字符串定义)
section .data ; 严格匹配你的输入格式,注意空格和占位符对应 epsilon_format: db "epsilon = %g", 0 order_format: db " order = %d", 0 coeff_format: db " coeff %d = %lf %lf", 0 initial_format: db " initial = %lf %lf", 0 err_msg: db "Input error!", 10, 0
2. 未初始化数据段(存储输入的变量)
根据你的输入需求,每个变量要分配足够的空间:
section .bss epsilon resq 1 ; double类型,占8字节 order resd 1 ; int类型,占4字节 coeff2 resq 2 ; 两个double,对应coeff 2的两个值 coeff1 resq 2 ; coeff 1的两个值 coeff0 resq 2 ; coeff 0的两个值 initial resq 2 ; initial的两个值
3. 代码段(核心逻辑)
section .text extern scanf, printf global main main: ; 初始化栈帧,同时保证16字节对齐 push rbp mov rbp, rsp sub rsp, 16 ; main被调用时RSP是8字节对齐(call压了返回地址),减8后变成16字节对齐 ; --- 第一次调用scanf:读取epsilon --- mov rdi, epsilon_format ; 第一个参数:格式字符串地址 mov rsi, epsilon ; 第二个参数:存储epsilon的变量地址 xor eax, eax ; 告诉scanf没有用XMM寄存器传浮点参数 call scanf cmp eax, 1 ; 检查是否成功读取1个值 jne input_error ; --- 第二次调用scanf:读取order --- mov rdi, order_format mov rsi, order xor eax, eax call scanf cmp eax, 1 jne input_error ; --- 第三次调用scanf:读取coeff 2 = 2.0 0.0 --- mov rdi, coeff_format ; 格式字符串 mov rsi, 2 ; 第一个占位符%d的参数:2 mov rdx, coeff2 ; 第二个占位符%lf的地址:coeff2的第一个值 mov rcx, coeff2 + 8 ; 第三个占位符%lf的地址:coeff2的第二个值(double占8字节) xor eax, eax call scanf cmp eax, 3 ; 检查是否成功读取3个值(int + 2个double) jne input_error ; --- 后续读取coeff1、coeff0、initial的逻辑 --- ; 读取coeff 1 mov rdi, coeff_format mov rsi, 1 mov rdx, coeff1 mov rcx, coeff1 + 8 xor eax, eax call scanf cmp eax, 3 jne input_error ; 读取coeff 0 mov rdi, coeff_format mov rsi, 0 mov rdx, coeff0 mov rcx, coeff0 + 8 xor eax, eax call scanf cmp eax, 3 jne input_error ; 读取initial mov rdi, initial_format mov rsi, initial mov rdx, initial + 8 xor eax, eax call scanf cmp eax, 2 jne input_error ; --- 读取成功,可添加后续处理逻辑 --- mov eax, 0 ; 返回0表示成功 leave ret input_error: ; 打印错误信息 mov rdi, err_msg call printf mov eax, 1 ; 返回1表示错误 leave ret
你之前第三次scanf崩的常见原因排查
- 栈对齐问题:如果调用
scanf前RSP不是16字节对齐,scanf内部用SSE指令时会直接触发段错误——这是最容易踩的坑,所以我们在main开头用sub rsp,16保证对齐。 - 参数传递错误:比如第三次调用时,你可能把coeff的地址放到了错误的寄存器(比如用了
rsi存地址,但rsi应该放%d对应的参数2),导致scanf写入无效内存。 - 变量空间不足:比如你给
coeff2只分配了resq 1,但需要存两个double,写入第二个值时就会越界,触发段错误。 - 格式字符串不匹配:比如把
%lf写成了%f(虽然scanf里%f对应float,但你用的是double变量,必须用%lf或者%g),或者占位符数量和参数数量不对应。
编译运行方法
用nasm和gcc编译:
nasm -f elf64 your_program.asm -o your_program.o gcc your_program.o -o your_program ./your_program
然后输入你指定的测试输入,就能正常读取了。
内容的提问来源于stack exchange,提问作者Rotem Barak
相关产品推荐
相关产品推荐

