首先,你对自己的要求有点不清楚。当我第一次读你的文章时,看起来你正在尝试用汇编程序()编写一个完整的“正则表达式”例程。但仔细观察,似乎您真正所做的只是在汇编器中“硬编码”这个非常具体的正则表达式搜索。这种误解可能就是这个问题没有得到任何答复的原因。
其次,你应该与this guy谁显然和你在同一个班级。也许你们两个可以分享笔记。
第三,有人应该与你的导师讨论他的作业。使用 gcc 的“内联汇编”来教授汇编可能是最难的方法。他讨厌他的学生吗?我对他提供的“模板”印象不深,(显然?)您不允许更改。我可以看到至少有六件事是我要改变的。
第四,你说正则表达式字符串“[ab][^r]+r”应该打印出来5, 10
对于“fqr b qabxx xryc pqr”。我不知道怎么会这样。比赛确实从(从零开始)5 开始,但不是在位置 10 结束:
1 2
0123456789012345678901
fqr b qabxx xryc pqr
^ ^
start end
末尾是位置 15。匹配的字符串 (b qabxx xr
) is 11个字符长,所以显然您并不是在寻找长度。第二个“起点”出现在位置 8,第三个“起点”出现在位置 9,并且还有多个可能的终点。这些都没有解释你应该在哪里得到“10”。我假设你的意思是5, 15
.
综上所述,处理[ab][^r]+r
基本上分为 3 个部分:
-
[ab]
查找“a”或“b”。如果找不到字符串末尾,则在遇到字符串结尾时错误退出。
-
[^r]+
如果 (1) 后面紧跟着的字母是“r”,则转到 1。
-
r
遍历字符串的其余部分并在下一个“r”处成功退出,或者在字符串末尾错误退出。
如果您不明白为什么是这些部分,请尝试使用https://regex101.com/r/E3nI1F/1(它可以让您尝试各种正则表达式搜索字符串以查看找到的内容)。
看看您当前的代码,我认为您没有正确处理(2)或(3)(实际上,我认为您根本没有处理它们)。虽然我会在您的代码中更改其他内容,但也许调整应该等到代码正常工作为止。
鉴于这是家庭作业,我不热衷于仅仅发布我的代码。如果你只是复制/粘贴我的作品,你就不会学到任何东西。
如果您在添加 2 和 3 的工作后想编辑您的问题,我可以再次审核或提供更多建议。如果您发布工作副本,我可以分享我的副本,我们可以对它们进行比较。
----------- 编辑 1 --------------
我的老师似乎并不讨厌我们
哦?考虑这段代码(您的简化版本):
asm volatile (
"xor %0, %0;"
"mov %1, %2"
:"=r" (x), "=r" (y)
:"r" (s));
看起来很简单,对吧?清零x
,并复制s
to y
。然而,由于所谓的“早期破坏”(参见'&'
on https://gcc.gnu.org/onlinedocs/gcc/Modifiers.html), 这是possible(不保证)优化时,gcc 将为 %0 和 %2 (或者可能是 %1 和 %2)选择相同的寄存器。因此,当您将 %0 清零时,您也可能将 %2 清零。
可以通过添加 & 符号以确保不重叠来解决此问题:
:"=&r" (x), "=&r" (y)
但你期望如何know这?了解这个细节并不能帮助你学习汇编程序。这只是 gcc 的内联汇编如何工作的一个奇怪的怪癖。如果您正在编写一个实际的汇编例程(这是我推荐的),您永远不需要知道这一点。
如果您使用符号名称,这不是更容易阅读吗?
asm volatile (
"xor %[x], %[x];"
"mov %[y], %[s]"
: [x] "=&r" (x), [y] "=&r" (y)
: [s] "r" (s));
I发现它更容易阅读。但这是另一件与汇编语言无关的事情。这只是一个关于如何在使用 gcc 时将内联 asm 推入 c 代码的技巧(你应该这样做几乎从不 do).
还有什么?此模板的一些其他问题:volatile
限定符不属于这里。它缺少"cc"
破坏。还有"memory"
破坏。最终你会破坏比你需要的更多的寄存器。哦,为什么不直接告诉人们用-masm=intel
并避免“.intel_syntax noprefix;”和“.att_syntax 前缀;”垃圾(还有更多海湾合作委员会的怪癖)。
使用汇编语言can有用的。我并不是想说事实并非如此。但尝试使用 gcc 的内联汇编充满了怪癖。由于用纯汇编程序编写的函数可以从 C 代码中调用,而且该方法没有这些问题,所以我只能得出结论,你被迫这样做是因为你对他/她很刻薄并且他/她讨厌你。
----------- 编辑2 --------------
既然您已经发布了工作代码(假设您已经修复了"arb r"
),让我提供我的:
#include <stdio.h>
int main(int argc, char *argv[])
{
const char *s = "fqr b qabxx xryc pqr"; // Succeeds with 5,11
int x, y;
// Assumes s is not NULL.
// Return y=-1 on not found.
asm volatile (
".intel_syntax noprefix\n\t"
"lea ebx, [%2-1]\n\t" // ebx is pointer to next character.
"mov ecx, %2\n\t" // Save this since we aren't using earlyclobber...
"mov %1, -1\n" // ...so at this point, %2 might be dead.
// Note that ebx starts at s-1.
"Phase1:\n\t"
"inc ebx\n\t"
"mov al, [ebx]\n\t" // Read next byte.
"test al, al\n\t"
"jz Done\n\t" // End of string.
// Check for [ab]
"cmp al, 'a'\n\t"
"je Phase2\n\t"
"cmp al, 'b'\n\t"
"jne Phase1\n"
// Phase 2 - Found [ab], check for [^r]+
"Phase2:\n\t"
"mov al, byte ptr [ebx+1]\n\t"
"test al, al\n\t"
"jz Done\n\t" // End of string.
"cmp al, 'r'\n\t"
"je Phase1\n\t" // Found [^r]+, go look for another [ab]
"mov %0, ebx\n\t"
// Found [ab], and no [^r]+. Find r.
"mov ebx, 1\n"
"Phase3:\n\t"
"mov al, [%0 + ebx]\n\t" // Read next byte.
"inc ebx\n\t"
"test al, al\n\t"
"jz Done\n\t" // End of string.
"cmp al, 'r'\n\t"
"jne Phase3\n\t"
// Found r.
"sub %0, ecx\n\t" // Set (x)
"mov %1, ebx\n"
"Done:\n"
".att_syntax prefix"
:"=r" (x), "=r" (y)
:"r" (s)
:"eax", "ebx", "ecx", "edx"
);
printf("%d, %d \n", x, y);
return 0;
}
它更短,并且不需要那么多寄存器(没有 edx)。虽然它还可以进一步调整,但它是解决家庭作业问题的可靠解决方案。
如果你被允许改变框架,它可能会好一点:
// Returns y = -1 if no regex match is found.
__asm__ (
// ---------------------------------
// Phase1 - look for [ab]
"mov %[x], %[s]\n" // Pointer to next char to read
"Phase1:\n\t"
"mov al, [%[x]]\n\t" // Read next byte
"test al, al\n\t"
"jz NotFound\n\t" // Hit end of string
"inc %[x]\n\t"
"cmp al, 'a'\n\t"
"je Phase2\n\t"
"cmp al, 'b'\n\t"
"jne Phase1\n"
// ---------------------------------
// Phase2 - Found [ab], Check for [^r]+
"Phase2:\n\t"
// x is pointing to the byte after [ab]
"mov al, [%[x]]\n\t" // Read next byte.
"test al, al\n\t"
"jz NotFound\n\t" // Hit end of string
"cmp al, 'r'\n\t"
"je Phase1\n\t" // Found [^r]+, go look for another [ab]
// ---------------------------------
// Phase3 - Found [ab], and no [^r]+. Now find r.
// x went 1 too far back in Phase1
"dec %[x]\n\t"
// We know there is 1 non-r character after [ab]
"mov %[y], 1\n"
"Phase3:\n\t"
"mov al, [%[x] + %[y]]\n\t" // Read next byte.
"inc %[y]\n\t"
"test al, al\n\t"
"jz NotFound\n\t" // End of string.
"cmp al, 'r'\n\t"
"jne Phase3\n\t"
// Found +r.
"sub %[x], %[s]\n\t" // Set x to offset
"jmp Done\n"
"NotFound:\n\t"
"mov %[y], -1\n"
"Done:"
: [x] "=&r" (x), [y] "=&r" (y)
: [s] "r" (s)
: "eax", "cc", "memory");
主要变化是:
- 假设代码是用
-masm=intel
.
- 更改自
"=r"
to "=&r"
。这保证了x
, y
and s
所有这些最终都在单独的寄存器中。
- 使用符号名称。而不是参考
x
as %0
,我们可以使用这个名字%[x]
.
- 由于此代码读取内存并修改标志,因此我添加了“cc”和“内存”破坏者。
- 删除不需要的
volatile
.
这会破坏更少的寄存器(只有 eax)。虽然使用寄存器并不是“坏”(没有它们很难做很多事情),但是您保留的寄存器越多,以便在您的应用程序中使用。asm
,编译器在调用代码之前释放这些寄存器所需要做的工作就越多。自从x
, y
and s
are already在寄存器中(由于"r"
),利用它们可以简化代码。