【问题标题】:Convert string to upper case将字符串转换为大写
【发布时间】:2025-11-26 22:50:02
【问题描述】:

我试图在汇编中迭代一个字符串,并将小写字母更改为大写,并在字符串为 0 时停止,但似乎有些错误(我似乎缺少一个概念)。我无法弄清楚问题是什么或发生了什么。

这是我目前所拥有的:

Upper:
        movq    (%rbx), %rcx
        movq    $0, %rdi
        call    check
        call    fin
        add     %rdi, %rax
        ret

    fin:
        cmpb    $0x0, %r9b
        jne     check
        ret

    check:
        movb    (%rcx, %rdi), %r9b
        cmp     $'Z', %r9b
        jg      toUpper
        jmp     next

    toUpper:
        sub     %r9b, 0x20
        jmp     next

    next:
        incq    %rdi
        jmp     fin

【问题讨论】:

  • 你能说明你如何称呼Upper吗?它似乎不遵循常见的调用约定。

标签: string assembly x86-64 att


【解决方案1】:

看起来,您的代码有点复杂,很难遵循您尝试实现的算法。

在处理此类问题时,通常先用 C 或伪代码写下基本算法会有所帮助:

  • 对于每个字符c
    • 如果c 是空字节:完成
    • 如果c 低于'a':忽略
    • 如果c 高于'z':忽略
    • Else:将'A''a' 的差异添加到c

这几乎直接转化为以下汇编程序:

upper:

    ; Read next character
    mov (%rdi), %al

    ; Test for zero byte
    test %al, %al
    je done

    ; Test for <'a' and >'z'
    cmp $'a', %al
    jl next
    cmp $'z', %al
    jg next

    ; We have a lower case character, so convert to upper case
    sub $0x20, %al ; Difference between 'A' and 'a'
    mov %al, (%rdi)

next:

    ; Increment pointer
    inc %rdi
    jmp upper

done:
    ret

这个函数需要rdi中的字符串指针,因此可以直接从C调用:

#include <stdio.h>

extern void upper(char *str);

int main()
{
    char str[] = "abc 123 <=>? 987 xyz!";
    upper(str);
    printf("Upper case: %s\n", str);

    return 0;
}

输出

Upper case: ABC 123 <=>? 987 XYZ!

【讨论】:

  • 添加 0xe0 是写-0x20 的一种不寻常的方式。通常你会sub $0x20, %al 或者只是用and $~0x20, %al 屏蔽小写位。这些都更直观。此外,您可以通过将 load / test/jnz 放在底部来避免在循环内使用无条件的jmp。 (并跳转到 for 循环条目,或剥离第一次迭代的那部分)。 Why are loops always compiled into "do...while" style (tail jump)?
  • 感谢您指出这一点,我已经修复了添加内容。我只是计算了差异,并没有注意到这个数字是负数:D
  • 关于循环结构:我同意你的观点,但在这种情况下是故意的,尽可能靠近伪代码。在实践中,do...while 方法显然是这里的方法(或者只是用 C 编写程序并使用编译器......)。
  • 是的,尽管我认为 do{}while 循环是在 asm 中循环的正常/惯用方式,但我还是投了赞成票。但是有足够的条件逻辑,添加一个循环入口分支可能会让那些代码还不是很明显的人更难理解。顺便说一句,What is the idea behind ^= 32, that converts lowercase letters to upper and vice versa? 有更多信息,包括如何使用 sub/cmp/ja 而不是 2x cmp/jcc 进行范围检查。