程序集循环遍历字符串以计算字符数
Assembly loop through a string to count characters
我尝试编写一个汇编代码来计算字符串中有多少个字符,但出现错误。
代码,我用的是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 loop
和 print:
后的分号。
此外,您的 asm 也无法正常工作。您将指向 s
的指针移动到 eax 中,但随后您用 mov al,[eax]
覆盖了它。所以下一次循环,eax 不再指向字符串。
当你解决这个问题时,你需要考虑这样一个事实,即每次通过循环都需要更改 eax 以指向下一个字符,否则 mov al,[eax]
会继续读取相同的字符。
由于您尚未接受答案(通过单击左侧的复选标记),因此还有时间再进行一次编辑。
通常我不会 "do people's homework",但已经有几天了。据推测,作业的截止日期已经过去。在这种情况下,这里有一些解决方案,既适用于 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 确保任何 'pending' 写入 s
在执行之前刷新到内存汇编。它还使用格式 (\n\t),因此使用 -S
构建的输出是可读的。
3) 使用更少寄存器的高级版本(无 eax
),检查以确保 s
不为 NULL(returns -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 行 [which are'实际上没有说明]).
有一些方法可以进一步改进这一点(在标签上使用 %=
以避免可能的重复符号问题,使用本地标签 (.L
),甚至将其写成适用于 both -masm=intel
and -masm=att
, etc.), 但我敢说这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 中的 'NULL pointer' 检查,但会执行 Peter 推荐的 'pointer increment'。它还避免了潜在的重复符号(使用 %=
),并使用 'local' 标签(以 .L
开头的标签)来避免将额外的符号写入目标文件。
从 "performance" 的角度来看,这可能会稍微好一些(我没有计时)。然而,从 "school project" 的角度来看,#3 的清晰度似乎是更好的选择。从 "what would I write in the real world if for some bizarre reason I HAD to write this in asm instead of just using a standard c function" 的角度来看,我可能会查看使用情况,除非这对性能至关重要,否则我很想选择 #3 以方便将来的维护。
我尝试编写一个汇编代码来计算字符串中有多少个字符,但出现错误。
代码,我用的是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 loop
和 print:
后的分号。
此外,您的 asm 也无法正常工作。您将指向 s
的指针移动到 eax 中,但随后您用 mov al,[eax]
覆盖了它。所以下一次循环,eax 不再指向字符串。
当你解决这个问题时,你需要考虑这样一个事实,即每次通过循环都需要更改 eax 以指向下一个字符,否则 mov al,[eax]
会继续读取相同的字符。
由于您尚未接受答案(通过单击左侧的复选标记),因此还有时间再进行一次编辑。
通常我不会 "do people's homework",但已经有几天了。据推测,作业的截止日期已经过去。在这种情况下,这里有一些解决方案,既适用于 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 确保任何 'pending' 写入 s
在执行之前刷新到内存汇编。它还使用格式 (\n\t),因此使用 -S
构建的输出是可读的。
3) 使用更少寄存器的高级版本(无 eax
),检查以确保 s
不为 NULL(returns -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 行 [which are'实际上没有说明]).
有一些方法可以进一步改进这一点(在标签上使用 %=
以避免可能的重复符号问题,使用本地标签 (.L
),甚至将其写成适用于 both -masm=intel
and -masm=att
, etc.), 但我敢说这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 中的 'NULL pointer' 检查,但会执行 Peter 推荐的 'pointer increment'。它还避免了潜在的重复符号(使用 %=
),并使用 'local' 标签(以 .L
开头的标签)来避免将额外的符号写入目标文件。
从 "performance" 的角度来看,这可能会稍微好一些(我没有计时)。然而,从 "school project" 的角度来看,#3 的清晰度似乎是更好的选择。从 "what would I write in the real world if for some bizarre reason I HAD to write this in asm instead of just using a standard c function" 的角度来看,我可能会查看使用情况,除非这对性能至关重要,否则我很想选择 #3 以方便将来的维护。