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

汇编中调用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崩的常见原因排查

  1. 栈对齐问题:如果调用scanf前RSP不是16字节对齐,scanf内部用SSE指令时会直接触发段错误——这是最容易踩的坑,所以我们在main开头用sub rsp,16保证对齐。
  2. 参数传递错误:比如第三次调用时,你可能把coeff的地址放到了错误的寄存器(比如用了rsi存地址,但rsi应该放%d对应的参数2),导致scanf写入无效内存。
  3. 变量空间不足:比如你给coeff2只分配了resq 1,但需要存两个double,写入第二个值时就会越界,触发段错误。
  4. 格式字符串不匹配:比如把%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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 07:49:58