我想比较两个4字符的字符串。例如“ A”,“ T”,“ T”,“ C” 反对“ A”,“ T”,“ T”,“ c”。我将这些字符存储在c ++中的数组中,我想在指令中比较这两个单词。此外,我不想使用循环进行比较。如何将这些单词存储在“ eax”和“ ebx”寄存器中并相互比较?
int _tmain()
{
char b[3],a[3];
b[0]='A',b[1]='T',b[2]='C',b[3]='G';
a[0]='A',a[1]='T',a[2]='C',a[3]='G';
__asm
{
movzx eax,b[1] //here i want to load b to eax
}
getchar();
return 0;
}
如果在同一条指令中还有比较两个单词的想法,请分享谢谢。
答案 0 :(得分:3)
此答案的其余部分假设您 使用内联asm进行某些作业(因为它不会比智能编译器内联的效率更高)。 4字节的memcmp
)。有关4字节memcmp
的gcc / clang的信息,请参见@MartinYork的答案。但是令人惊讶的是,只有gcc7和更高版本内联了恒定大小的memcmp
。用Clang至少可以恢复到3.5。
MSVC 2017还内联memcmp
以获得恒定的4字节大小,并内联std::array
运算符==,从而产生与gcc / clang相同的asm。 (我没有测试早期版本)。请参见纯C ++版本on the Godbolt compiler explorer。
从char数组中加载dword的必要语法是dword ptr
大小覆盖。
// true for equal, false for not-equal
bool foo()
{
//char a[] = "ACTG";
char a[] = {'A', 'C', 'T', 'G'};
char b[] = {'A', 'T', 'T', 'G'};
_asm {
mov eax, dword ptr a // mov eax, a would complain
cmp eax, dword ptr b
sete al // al= 0 or 1 depending on ZF, the "e" condition like je
}
// falling off the end of a non-void function implicitly returns EAX
// apparently this is supported in MSVC even when inlining
}
作为一个完整的功能,该编译如下, with MSVC 19, 2017, with -Ox
on the Godbolt compiler explorer :
;; define a couple assembler constants for use
_a$ = -8 ; size = 4
_b$ = -4 ; size = 4
foo PROC
sub esp, 8
mov DWORD PTR _a$[esp+8], 1196704577 ; 47544341H
mov DWORD PTR _b$[esp+8], 1196708929 ; 47545441H
;; inline asm block starts here
mov eax, DWORD PTR _a$[esp+8]
cmp eax, DWORD PTR _b$[esp+8]
sete al
;; and ends here
add esp, 8
ret 0
foo ENDP
前2条mov
指令由编译器生成,使用dword MOV立即将4字节数组存储到堆栈中。
如果您要返回0 /非0 int
而不是0/1 bool
,则可以使用@P__J__建议mov
/ sub
cmp
之后检查标志。两个相等的双字将使寄存器为0,其他情况则不会。 (xor
具有相同的属性。)
如果您想比较作为函数arg获得的char*
的4个字节,它将是一个指针,而不是C数组,因此您必须自己将指针加载到内联asm中的寄存器中。 (即使编译器已经在寄存器中包含了指针; MSVC内联汇编语法也基本上会占用较小的块,因为它会强制对输入和输出进行存储/重载往返(约5个周期的延迟),除非您可以明显使用支持的将EAX保留下来并掉落非无效函数的末尾的方法,另请参见What is the difference between 'asm', '__asm' and '__asm__'?与GNU C内联汇编的比较,这使得在寄存器中请求输入并产生多个输出变得容易在寄存器中,允许编译器进行尽可能多的优化,当然它仍然会破坏常量的传播;如果您使用memcmp
,则编译器只能return 0
,因为数组具有编译时常量内容。 {3}})
无论如何,这是比较函数args的前4个字节所得到的:
char bar(char *a, char *b)
{
// a and b are pointers, not arrays
_asm {
mov eax, a // loads the address
mov eax, [eax] // loads 4 bytes of data
mov ecx, b
cmp eax, [ecx]
sete al
}
}
bar PROC
mov eax, DWORD PTR _a$[esp-4]
mov eax, DWORD PTR [eax]
mov ecx, DWORD PTR _b$[esp-4]
cmp eax, DWORD PTR [ecx]
sete al
ret 0
如果您使用-Gv
进行编译或使用更好的调用约定以在寄存器中传递args的方式进行编译,实际上是更糟糕的:编译器必须将指针args溢出到asm的堆栈中重新加载它们,而不是将其转换为reg-reg动作。 AFAIK,无法通过强制转换或任何方式使编译器为您将指针加载到寄存器中,因此您可以直接在嵌入式asm中引用数组内容。
答案 1 :(得分:2)
首先,您的阵列存在严重问题。您将数组定义为容纳3个元素,但是尝试将4个元素填充到数组中。这真的很糟糕,并且会导致不确定的行为。
此外...丢下组件! lib函数将(几乎在所有情况下)执行您可以在汇编中执行的功能。换句话说-只需使用VAR
赞:
memcmp
答案 2 :(得分:2)
我要说的是在汇编中这样做是一个坏主意。
您应该使用高级语言构造。这样可以使代码具有可移植性,并且当需要推送时,编译器将在诸如此类的窥视孔优化中击败“大多数”人。
因此,我检查了g ++的输出以查看其生成的程序集。
#include <array>
#include <iostream>
bool testX(int a, int b);
bool testY(std::array<char, 4> const& a, std::array<char, 4> const& b);
bool testZ(char const(&a)[4], char const(&b)[4]);
int main()
{
{
int a = 'ATCG';
int b = 'ATCG';
if (testX(a, b)) {
std::cout << "Equal\n";
}
}
{
std::array<char, 4> a {'A', 'T', 'C', 'G'};
std::array<char, 4> b {'A', 'T', 'C', 'G'};
if (testY(a, b)) {
std::cout << "Equal\n";
}
}
{
char a[] = {'A', 'T', 'C', 'G'};
char b[] = {'A', 'T', 'C', 'G'};
if (testZ(a, b)) {
std::cout << "Equal\n";
}
}
}
启用优化后,我们会从clang(通常是最近的gcc on the Godbolt compiler explorer)获得不错的asm。 (如果输入可以编译,则上面的main
可以优化比较,因为输入是编译时常量。)
bool testX(int a, int b)
{
return a == b;
}
# gcc and clang -O3 asm output
testX(int, int):
cmpl %esi, %edi
sete %al
ret
#include <cstring>
bool testZ(char const(&a)[4], char const(&b)[4])
{
return std::memcmp(a, b, sizeof(a)) == 0;
}
# clang, and gcc7 and newer, -O3
testZ(char const (&) [4], char const (&) [4]):
movl (%rdi), %eax
cmpl (%rsi), %eax
sete %al
retq
#include <array>
bool testY(std::array<char, 4> const& a, std::array<char, 4> const& b)
{
return a == b;
}
# only clang does this. gcc8.2 actually calls memcmp with a constant 4-byte size
testY(std::array<char, 4ul> const&, std::array<char, 4ul> const&):
movl (%rdi), %eax
cmpl (%rsi), %eax
sete %al
retq
因此,用于比较4字节对象的std :: array和memcmp都使用clang生成相同的代码,但仅使用gcc memcmp
会产生很好的效果。
当然,该函数的独立版本实际上必须产生0/1整数,而不是仅设置jcc
的标志以直接分支。这些函数的调用者必须在分支之前先test %eax,%eax
。但是,如果编译器可以内联这些函数,那么开销就会消失。
答案 3 :(得分:0)
类似这样的东西:
asm{
mov eax,'A'
mov ebx,'C'
cmp eax,ebx
JAE input_a
** here you print that 'A' <= 'C' **
jump endofMain
input_a:
** here you print that 'A' >= 'C' **
endofMain:
}
return 0;
答案 4 :(得分:-1)
** IMPORTANT: UPGRADE PROBLEM: The data files need to be fully upgraded to version 3.6 before attempting an upgrade to 4.0; see http://dochub.mongodb.org/core/4.0-upgrade-fcv for more details.
2018-08-01T00:15:50.220+0200 I NETWORK [initandlisten] shutdown: going to close listening sockets...
2018-08-01T00:15:50.220+0200 I NETWORK [initandlisten] removing socket file: /tmp/mongodb-27017.sock
2018-08-01T00:15:50.224+0200 I STORAGE [initandlisten] WiredTigerKVEngine shutting down
2018-08-01T00:15:50.303+0200 I STORAGE [initandlisten] Downgrading WiredTiger datafiles.
2018-08-01T00:15:50.501+0200 I STORAGE [initandlisten] WiredTiger message [1533075350:501686][3594:0x7fffb492e380], txn-recover: Main recovery loop: starting at 14/3712
2018-08-01T00:15:50.598+0200 I STORAGE [initandlisten] WiredTiger message [1533075350:598867][3594:0x7fffb492e380], txn-recover: Recovering log 14 through 15
2018-08-01T00:15:50.664+0200 I STORAGE [initandlisten] WiredTiger message [1533075350:663976][3594:0x7fffb492e380], txn-recover: Recovering log 15 through 15
2018-08-01T00:15:50.715+0200 I STORAGE [initandlisten] WiredTiger message [1533075350:715398][3594:0x7fffb492e380], txn-recover: Set global recovery timestamp: 0
2018-08-01T00:15:51.002+0200 I STORAGE [initandlisten] shutdown: removing fs lock...
2018-08-01T00:15:51.005+0200 I CONTROL [initandlisten] now exiting
2018-08-01T00:15:51.005+0200 I CONTROL [initandlisten] shutting down with code:62