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

64位Windows NASM中连续调用printf两次输出结果不一致问题

问题分析与解决

核心问题:易失性寄存器被破坏

你遇到的问题根源是r11属于Windows x64调用约定中的易失性寄存器——这类寄存器(rax、rcx、rdx、r8-r11)在调用函数后,值不会被保留。第一次调用printf时,printf内部会使用r11,直接覆盖了你存在里面的argc值,所以第二次调用时r11已经不是原来的参数数量了。

栈操作的必要性

你提到的函数首尾栈操作,对应Windows x64调用约定的两个强制要求:

  1. 阴影空间(Shadow Space):所有函数必须在栈上预留至少32字节(4×8字节)的空间,供被调用函数用来备份rcx/rdx/r8/r9这四个寄存器参数。printf作为标准库函数,会依赖这个空间完成内部操作,不预留就会触发段错误。
  2. 16字节栈对齐:调用任何函数前,栈指针rsp必须是16字节对齐。进入main时,rsp因为call指令压入返回地址,处于8字节偏移状态;push rbp后rsp再减8,回到16字节对齐,后续sub rsp, 32也会保持对齐,满足调用要求。

修复方案

方案1:使用非易失性寄存器存储argc

Windows x64的非易失性寄存器包括rbx、rbp、r12-r15,这些寄存器在函数调用后会被保留。注意使用前要保存原值,调用结束后恢复:

global main
extern  printf

section .data
    fmt_num: db '%ld Params...', 10, 0

section .text
main:
    push rbp
    mov rbp, rsp
    sub rsp, 8*4
    push rbx ; 保存rbx的原始值,遵守调用约定

    mov rbx, rcx ; 将argc存入非易失性寄存器rbx

    mov rcx, fmt_num
    mov rdx, rbx
    call printf      ; 第一次输出正确

    mov rcx, fmt_num
    mov rdx, rbx     ; rbx的值未被printf修改
    call printf      ; 输出正确结果

    pop rbx ; 恢复rbx的原始值
    add rsp, 8*4
    mov rax, 0
    pop rbp

    ret

方案2:将argc存入栈局部变量

把参数存在栈的局部空间里,完全不受寄存器破坏的影响:

global main
extern  printf

section .data
    fmt_num: db '%ld Params...', 10, 0

section .text
main:
    push rbp
    mov rbp, rsp
    sub rsp, 8*4

    mov [rbp-8], rcx ; 将argc存入栈上的局部变量位置

    mov rcx, fmt_num
    mov rdx, [rbp-8]
    call printf

    mov rcx, fmt_num
    mov rdx, [rbp-8] ; 直接从栈中读取原始argc值
    call printf

    add rsp, 8*4
    mov rax, 0
    pop rbp

    ret

补充说明

  • 你测试时用mov rdx,23能正常运行,是因为直接给rdx赋值,没有依赖被破坏的寄存器,避开了核心问题。
  • 格式符从%ld改%d无效,是因为问题本质是寄存器值被破坏,和格式符无关——不管用哪种格式,只要传入的是错误的寄存器值,输出就会异常。

内容的提问来源于stack exchange,提问作者Digit

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 18:13:21