c - 汇编循环遍历字符串来计算字符数

标签 c string gcc assembly intel-syntax

我尝试编写一个汇编代码来计算字符串中有多少个字符,但出现错误。

代码,我使用gcc和intel_syntax

#include <stdio.h>

int main(){
char *s = "aqr  b qabxx xryc pqr";
int x;

asm volatile (
    ".intel_syntax noprefix;"
    "mov eax, %1;"
    "xor ebx,ebx;"
    "loop:"
        "mov al,[eax];"
        "or al, al;"
        "jz print;"
        "inc ebx;"
        "jmp loop"
    "print:"
    "mov %0, ebx;"
    ".att_syntax prefix;"
    : "=r" (x)
    : "r" (s)
    : "eax", "ebx"
);

    printf("Length of string: %d\n", x);
    return 0;

}

我得到了错误:

Error: invalid use of register

最后我想制作一个程序,它搜索正则表达式模式([pq][^a]+a)并打印它的起始位置和长度。我用 C 语言编写了它,但我必须让它在汇编中工作: 我的 C 代码:

#include <stdio.h>
#include <string.h>

int main(){
  char *s = "aqr  b qabxx xryc pqr";
  int y,i;
  int x=-1,length=0, pos = 0;

    int len = strlen(s);
    for(i=0; i<len;i++){
        if((s[i] == 'p' || s[i] == 'q') && length<=0){
            pos = i;
            length++;
            continue;
        } else if((s[i] != 'a')) && pos>0){
            length++;
        } else if((s[i] == 'a') && pos>0){
            length++;
            if(y < length) {
                y=length;
                length = 0;
                x = pos;
                pos = 0;    
            }
            else 
                length = 0;
                pos = 0;
        }
    }  

    printf("position: %d, length: %d", x, y);
    return 0;

}

最佳答案

您省略了jmp循环print:后面的分号。

<小时/>

此外,您的汇编程序将无法正常工作。您将指向 s 的指针移动到 eax 中,但随后用 mov al,[eax] 覆盖它。因此,下一次通过循环时,eax 不再指向该字符串。

当你解决这个问题时,你需要考虑这样一个事实:每次循环都需要更改 eax 以指向下一个字符,否则 mov al,[eax] 会继续读取相同的角色。

<小时/>

由于您尚未接受答案(通过单击左侧的复选标记),因此还有时间再进行一次编辑。

平时我不“给人家做作业”,但已经好几天了。大概作业的截止日期已经过去了。既然如此,这里有一些解决方案,既适合 OP 的教育,也适合 future 的 SO 用户:

1)遵循作业的(有点奇怪)限制:

asm volatile (
    ".intel_syntax noprefix;"
    "mov eax, %1;"
    "xor ebx,ebx;"
    "cmp byte ptr[eax], 0;"
    "jz print;"
    "loop:"
        "inc ebx;"
        "inc eax;"
        "cmp byte ptr[eax], 0;"
        "jnz loop;"
    "print:"
    "mov %0, ebx;"
    ".att_syntax prefix;"
    : "=r" (x)
    : "r" (s)
    : "eax", "ebx"
);

2)违反一些赋值规则来编写稍微更好的代码:

asm (
    "\n.intel_syntax noprefix\n\t"
    "mov eax, %1\n\t"
    "xor %0,%0\n\t"
    "cmp byte ptr[eax], 0\n\t"
    "jz print\n"
    "loop:\n\t"
        "inc %0\n\t"
        "inc eax\n\t"
        "cmp byte ptr[eax], 0\n\t"
        "jnz loop\n"
    "print:\n"
    ".att_syntax prefix"
    : "=r" (x)
    : "r" (s)
    : "eax", "cc", "memory"
);

这减少了 1 个寄存器(无 ebx)并省略了(不必要的) volatile 限定符。它还添加了“cc”clobber 来指示代码修改了标志,并使用“memory”clobber 来确保在执行 asm 之前对 s 的任何“待处理”写入都会刷新到内存中。它还使用格式 (\n\t),因此使用 -S 构建的输出是可读的。

3) 高级版本使用更少的寄存器(无eax),检查以确保s不为NULL(返回-1),使用符号名称并假设-masm=intel 这会产生更具可读性的代码:

__asm__ (
    "test %[string], %[string]\n\t"
    "jz print\n"
    "loop:\n\t"
        "inc %[length]\n\t"
        "cmp byte ptr[%[string] + %[length]], 0\n\t"
        "jnz loop\n"
    "print:"
    : [length] "=r" (x)
    : [string] "r" (s), "[length]" (-1)
    : "cc", "memory"
);

摆脱(任意且未经深思熟虑的)赋值约束,我们可以将其减少到 7 行(如果我们不检查 NULL,则为 5 行;如果我们不计算标签,则为 3 行 [实际上并非如此)说明])。

有一些方法可以进一步改进这一点(在标签上使用 %= 以避免可能的重复符号问题,使用本地标签 (.L),甚至编写它所以它适用于两者 -masm=intel-masm=att 等),但我敢说这 3 个中的任何一个都更好比原始问题中的代码。

<小时/>

好吧,库巴,我不确定您在接受答案之前还需要什么。尽管如此,它确实让我有机会包含 Peter 的版本。

4)指针增量:

__asm__ (
    "cmp byte ptr[%[string]], 0\n\t"
    "jz .Lprint%=\n"
    ".Loop%=:\n\t"
    "inc %[length]\n\t"
    "cmp byte ptr[%[length]], 0\n\t"
    "jnz .Loop%=\n"
    ".Lprint%=:\n\t"    
    "sub %[length], %[string]"
    : [length] "=&r" (x)
    : [string] "r" (s), "[length]" (s)
    : "cc", "memory"
);

这不会执行 #3 中的“空指针”检查,但它会执行 Peter 建议的“指针增量”。它还避免了潜在的重复符号(使用 %=),并使用“本地”标签(以 .L 开头)来避免将额外的符号写入目标文件.

从“性能”的角度来看,这可能会稍微好一些(我没有计时)。然而,从“学校项目”的角度来看,#3 的清晰度似乎是更好的选择。从“如果出于某种奇怪的原因我必须在 asm 中编写而不是仅使用标准 c 函数,我在现实世界中会写什么”的观点来看,我可能会考虑使用情况,除非这对性能至关重要,我很想选择#3,以方便将来的维护。

关于c - 汇编循环遍历字符串来计算字符数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40617824/

相关文章:

javascript 字符串到语句

Java:使用正则表达式从字符串中提取单个匹配组

c - C中的正则表达式匹配

GCC [对于 ARM] 强制没有浮点

c - 在 gcc 中出现错误但仍然成功运行

c - linux内核宏如何用作函数?

c - 混合程序(.asm + .cpp): modify small math program's code to include float input

c++ - 如何为 Linux 重新分发已编译的专有软件?

c - 在 mac 中使用 Carbon Framework 检测双显示器/显示器?

c - 如何使用 Msys2 和 MinGW 在 Windows 上构建 OpenLDAP 库?