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

如何基于x86汇编实现指定规则的string_cmp字符串比较函数

x86汇编实现符合要求的string_cmp函数

需求回顾

实现函数int string_cmp(const char* str1, const char* str2),按ASCII字母序比较两个字符串,返回规则:

  • str1 大于 str2:返回1
  • str1 等于 str2:返回0
  • str1 小于 str2:返回-1

原代码的问题

你的代码循环比较的逻辑框架是对的,但结果返回部分存在缺陷:sbb eax, eax只能生成0或-1,无法返回1,不符合需求。下面是修正后的完整可运行实现:

int string_cmp(const char* str1, const char* str2) {
    int result;

    asm volatile (
        "cld\n"                     // 清除方向标志,让字符串指令自动递增指针
        "1:\n"                      // 循环起始标签
        "lodsb\n"                   // 从str1取字节到al,rsi自增
        "scasb\n"                   // 比较al和str2的字节,设置标志位,rdi自增
        "jne 2f\n"                  // 字节不等则跳转到分支处理
        "test al, al\n"             // 检查当前字节是否为字符串结束符
        "jne 1b\n"                  // 未结束则回到循环继续比较
        "xor eax, eax\n"            // 字符串相等,结果设为0
        "jmp 3f\n"                  // 跳转到结束点
        "2:\n"                      // 字节不等的分支
        "mov eax, 1\n"              // 默认假设str1 > str2,设为1
        "jb 2f\n"                   // 若CF=1(说明str1字节更小),跳转到子分支
        "neg eax\n"                 // 子分支中把1取反为-1,对应str1 < str2
        "2:\n"
        "3:\n"                      // 结束标签
        : "=a" (result)             // 输出:eax的值存入result
        : "S" (str1), "D" (str2)    // 输入:str1放rsi,str2放rdi(直接用寄存器约束更规范)
        : "cc", "memory"            // 告知编译器:标志寄存器被修改,内存可能被访问
    );

    return result;
}

详细解释

1. 内联汇编约束说明

  • 输入约束:"S" (str1) 和 "D" (str2) 直接让编译器把str1放入rsi、str2放入rdi,比手动mov指令更符合GCC内联汇编的规范,避免寄存器冲突问题。
  • 输出约束:"=a" (result) 指定eax寄存器的最终值作为函数返回的result,因为x86系统调用和函数约定中,eax通常用来存返回值。
  • 破坏列表:"cc" 告诉编译器这段汇编修改了标志寄存器;"memory" 防止编译器对字符串内存做过度优化(比如提前缓存到寄存器,导致比较的不是原始内存值)。

2. 核心指令逐行解析

  • cld:必须执行的步骤,清除方向标志DF。当DF=0时,lodsb、scasb这类字符串操作指令会自动递增rsi/rdi指针;如果DF=1则会递减,这里我们需要从字符串开头向后遍历,所以必须清DF。
  • 1:\n:循环起始标签,1b表示向前跳转到最近的1标签,2f表示向后跳转到最近的2标签,这是x86汇编中标签跳转的常用写法。
  • lodsb:将rsi指向的字节加载到al寄存器,然后rsi自动加1(因为cld),每次循环取str1的下一个字符。
  • scasb:把al的内容和rdi指向的字节做比较,根据比较结果设置EFLAGS标志位(比如CF、ZF),然后rdi自动加1,每次循环取str2的下一个字符。
  • jne 2f:如果scasb的结果是字节不相等(ZF=0),跳转到标签2处理不等的情况。
  • test al, al:检查当前al里的字节是否为0(字符串结束符),test指令会设置ZF标志位(al=0时ZF=1)。
  • jne 1b:如果al不是0(ZF=0),说明还没到字符串结尾,跳回循环开头继续比较下一对字符。
  • xor eax, eax:如果循环正常结束(两个字符串同时到结尾),说明字符串完全相等,把eax设为0。
  • jmp 3f:直接跳转到结束标签,避免进入不等分支。
  • 不等分支处理:
    • mov eax, 1:先默认设置结果为1,对应str1字符大于str2字符的情况。
    • jb 2f:jb是基于CF标志的跳转,当scasb比较出al(str1的字节)小于原rdi指向的字节时,CF会被设为1,此时跳转到子标签2。
    • neg eax:如果没跳转,说明str1的字节更大,eax保持1;如果跳转过来,把eax的1取反成-1,对应str1小于str2的情况。
  • 3:\n:结束标签,汇编执行到这里完成,eax里的结果通过输出约束传给result变量。

原代码的缺陷分析

原代码中用sbb eax, eax生成结果,这个指令的逻辑是:如果CF=1,sbb eax,eax会得到-1;如果CF=0,得到0。但这只能覆盖相等(0)和小于(-1)的情况,无法返回1,所以必须添加判断逻辑区分“大于”和“小于”的场景,这就是修正代码的核心改动点。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 10:49:54