逐位移位除法C代码转汇编异常求排查
逐位移位除法汇编代码调试问题
我正在把一段无需除法运算符的逐位移位除法C代码转成汇编,C代码已验证可正常运行,但汇编仅在除数为1或被除数为0时结果正确,像5789217/8、10/5这类运算,商和余数始终为0,多次调试仍未定位问题。
原C代码
#include <stdio.h> #include <stdlib.h> int main (int argc, char *argv[]){ // 第一个命令行参数是被除数 long long dividend = atoll(argv[1]); // 第二个命令行参数是除数 long long divisor = atoll(argv[2]); long long remainder = 0; long long quotient = 0; if (divisor == 1) { quotient = dividend; remainder = 0; } else { // 循环32次(i从31到0) for(int i = 31; i >= 0; i--){ remainder <<= 1; remainder |= (dividend >> i) & 1; if (remainder >= divisor){ remainder -= divisor; quotient |= 1 << i; } } } printf("%lld / %lld = %lld R %lld\n", dividend, divisor, quotient, remainder); return 0; }
问题汇编代码
.global _start .data dividend: .long 100 divisor: .long 100 quotient: .long 0 remainder: .long 0 .text _start: movl quotient, %eax movl remainder, %edx movl divisor, %esi # previously ecx movl dividend, %ebx movl $31, %ecx # previously esi # 判断除数是否为1 cmpl $1, %esi jne for_start # 除数为1时,商等于被除数,余数为0 movl %ebx, %eax xor %edx, %edx jmp done for_start: cmpl $0, %ecx jle done # 循环体开始: shll $1, %edx # remainder左移1位 movl %ebx, %edi # 复制被除数到edi shrl %cl, %edi # 被除数右移i位(cl存当前i值) and $1, %edi # 取最低位 orl %edi, %edx # 将该位并入remainder # if判断开始 cmpl %esi, %edx # 比较remainder和divisor jle next_iter # 如果remainder < divisor,跳过减法 sub %esi, %edx # remainder -= divisor movl $1, %edi shll %cl, %edi # 1左移i位 orl %edi, %eax # 将该位并入quotient next_iter: decl %ecx # 循环计数器减1 jns for_start # 非负则继续循环 done: nop
问题分析与修复
核心问题:循环次数缺失
原C代码循环32次(i从31到0,包含0),但汇编代码的for_start标签下先判断cmpl $0, %ecx,当ecx=0时会直接跳转到done,跳过了i=0的循环体执行,导致整整一位数据未被处理,最终结果错误。
修复方案
移除for_start处的循环前置判断,让循环先执行体再判断是否继续,确保i从31到0的所有32次循环都被执行:
.global _start .data dividend: .long 100 divisor: .long 10 quotient: .long 0 remainder: .long 0 .text _start: movl quotient, %eax movl remainder, %edx movl divisor, %esi movl dividend, %ebx movl $31, %ecx # 判断除数是否为1 cmpl $1, %esi jne for_start # 除数为1时,商等于被除数,余数为0 movl %ebx, %eax xor %edx, %edx jmp done for_start: # 先执行循环体,再处理计数 shll $1, %edx # remainder左移1位 movl %ebx, %edi # 复制被除数到edi shrl %cl, %edi # 被除数右移i位(cl存当前i值) and $1, %edi # 取最低位 orl %edi, %edx # 将该位并入remainder cmpl %esi, %edx # 比较remainder和divisor jle next_iter # 如果remainder < divisor,跳过减法 sub %esi, %edx # remainder -= divisor movl $1, %edi shll %cl, %edi # 1左移i位 orl %edi, %eax # 将该位并入quotient next_iter: decl %ecx # 循环计数器减1 jns for_start # 当ecx >=0时继续循环(直到ecx变为-1时退出) done: # 添加退出系统调用,避免程序崩溃 movl $1, %eax xor %ebx, %ebx int $0x80
额外优化点(可选)
原C代码使用long long(64位)类型,但汇编用的是32位.long和32位寄存器。如果需要完全适配64位运算,需:
- 将数据段的
.long改为.quad - 使用64位寄存器(
rax、rdx、rbx、rsi、rcx、rdi) - 将循环初始值改为
$63(覆盖64位所有位) - 移位指令改用64位版本(
shlq、shrq)
内容的提问来源于stack exchange,提问作者user21894767
相关产品推荐
相关产品推荐

