汇编循环通过字符串来计算字符

我尝试制作一个汇编代码,计算字符串中有多少个字符,但是我得到一个错误。

代码,我使用gcc和intel_syntax

#include  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  #include  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') && length0){ 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 loopprint:之后省略了分号print:


你的asm也无法正常工作。 你将指针s移动到eax中,然后用mov al,[eax]覆盖它。 所以下一次通过循环,eax不再指向字符串了。

当你修复它时,你需要考虑这样一个事实:通过循环的每次传递需要改变eax指向下一个字符,否则mov al,[eax]继续读取相同的字符。


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

通常我不“做人的功课”,但已经过了几天。 据推测,作业的截止日期已经过去。 在这种情况下,这里有一些解决方案,既可用于OP的教育,也可用于未来的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等。 ),但我敢说这三个中的任何一个都比原始问题中的代码更好。


那么库巴,在你接受答案之前,我不确定你在这之后还有什么。 不过,它确实让我有机会加入彼得的版本。

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执行’NULL指针’检查,但它确实执行了Peter推荐的’指针增量’。 它还避免了潜在的重复符号(使用%= ),并使用“本地”标签(以.L开头的标签)来避免额外的符号被写入目标文件。

从“性能”的角度来看,这可能稍好一点(我还没有及时)。 然而,从“学校项目”的角度来看,#3的清晰度似乎是一个更好的选择。 从“在现实世界中我会写什么,如果出于某种奇怪的原因我不得不在asm中写这个而不仅仅是使用标准的c函数”的观点来看,我可能会考虑使用,除非这是性能关键的,我很想和#3一起去,以便日后维护。