x86_64 Intel汇编实现ft_putnbr仅首位数字输出正确的问题
代码错误定位
核心问题排查
- 递归调用后寄存器值被覆盖,导致数位输出错误
按照System V AMD64调用约定,rdx属于跨函数调用可被随意改写的临时寄存器。你在除法操作中得到的数位余数存在rdx中,紧接着就调用递归版本的_ft_putnbr——这个递归流程内部的除法操作、甚至调用_ft_putchar时的系统调用,都会直接覆盖rdx的值。等递归返回后再用mov rdi, rdx取数位时,拿到的根本不是之前计算的余数,全是流程运行留下的残留值,这就是正数输入时仅最高位(最外层直接走到打印分支的数位)正确、其余数位全输出0的原因。 - 未遵守调用约定保存保留寄存器
rbxrbx属于被调用者必须保护的寄存器,任何函数修改rbx前必须先入栈保存,返回前恢复原值。你在除法逻辑中直接mov rbx, 10修改寄存器值,没有做任何保存恢复,会直接破坏外层调用方的栈帧和局部变量,导致函数返回时栈指针错位,这就是负数输入时输出无意义大数值的原因。 - 无效栈操作逻辑错误
你在递归调用前写的mov rdi, 0 / push rdi没有实际作用:这步原本的设计意图是保存除法得到的余数,但你只把0压入栈,没有真正保存需要的rdx值,等于空做了栈操作。 - 未兼容整数溢出边界
当前neg rdi的逻辑,在输入64位有符号整数最小值时,会因为neg操作产生整数溢出,无法得到正确的绝对值,属于边界场景缺失。
修正后可正常运行的代码
BITS 64 extern _ft_putchar section .text global _ft_putnbr _ft_putnbr: push rbx ; 按调用约定保存rbx,避免破坏调用方寄存器 mov rbx, 10 ; 固定除数,无需重复赋值 _loop: cmp rdi, 0 jl _put_dash cmp rdi, rbx jae _div_ten jmp _put_dig _div_ten: mov rax, rdi xor rdx, rdx ; 清空rdx,准备做64位除法 div rbx ; 除法完成:rax=商,rdx=余数 push rdx ; 先把余数压栈,避免被递归调用覆盖 mov rdi, rax call _ft_putnbr ; 递归打印高位部分 pop rdx ; 递归返回后取出之前保存的数位 mov rdi, rdx jmp _put_dig _put_dash: push rdi mov rdi, '-' call _ft_putchar pop rdi neg rdi js _min_val_handle ; 处理neg溢出的整数最小值场景 jmp _loop _min_val_handle: ; 处理无法取绝对值的64位最小值:先打印个位8,再递归打印剩余部分 mov rdi, 8 call _ft_putchar mov rdi, 922337203685477580 call _ft_putnbr jmp _exit _put_dig: add rdi, 48 ; 转换为ASCII字符 call _ft_putchar _exit: pop rbx ; 恢复rbx原值 ret
内容的提问来源于stack exchange,提问作者kasder
相关产品推荐
相关产品推荐

