C++ 如何在内联组件c+中比较32位字符与32位字符+;
我想比较两个4字符字符串。例如“A”、“T”、“T”、“C” 反对“A”、“T”、“T”、“c”。我把这些字符存储在C++中的数组中,我想在指令中比较这两个词。此外,我不想使用循环进行比较。如何将这些单词存储在“eax”和“ebx”寄存器中并相互比较C++ 如何在内联组件c+中比较32位字符与32位字符+;,c++,assembly,visual-c++,x86,inline-assembly,C++,Assembly,Visual C++,X86,Inline Assembly,我想比较两个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 e
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;
}
如果在一条指令中有比较两个单词的其他方法,请与我们分享,谢谢。首先,您的数组存在严重问题。您将数组定义为容纳3个元素,但尝试将4个元素填充到数组中。这真的很糟糕,会导致未定义的行为 除此之外。。。放下组件!lib函数将(在几乎所有情况下)超出您在汇编中所能完成的功能。换句话说,只需使用
memcmp
比如:
大概是这样的:
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;
asm{
mov-eax,'A'
mov-ebx,'C'
cmp-eax,ebx
JAE输入_a
**在这里你可以打印出“A”=“C”**
endofMain:
}
返回0;
我要说的是,在汇编程序中执行此操作是个坏主意
您应该使用高级语言构造。这将允许代码是可移植的,当遇到紧急情况时,编译器将在任何类似这样的窥视孔优化中击败“大多数”人类
所以我检查了g++的输出,看看它生成了什么程序集
main.cpp
Z.cpp
Y.cpp
#包括
bool testY(std::数组常量&a,std::数组常量&b)
{
返回a==b;
}
Y.s
只有叮当声才能做到这一点。gcc8.2实际上使用恒定的4字节大小调用memcmp
testY(std::array const&,std::array const&):
movl(%rdi),%eax
cmpl(%rsi),%eax
sete%al
retq
因此,用于比较4字节对象的std::array和memcmp都会生成与clang相同的代码,但只有gccmemcmp
才能很好地优化
当然,该函数的独立版本实际上必须生成一个0/1整数,而不是仅仅为
jcc
设置直接分支的标志。在分支之前,这些函数的调用方必须测试%eax,%eax
。但是,如果编译器可以内联这些函数,那么开销就会消失。这个答案的其余部分假设您需要使用内联asm来完成一些家庭作业(因为它不会比智能编译器内联4字节memcmp
的效率更高)。有关gcc/clang对4字节memcmp
的作用,请参见@MartinYork的答案。但令人惊讶的是,只有gcc7和更高版本内联常量大小memcmp
。叮当声至少回到3.5管理这一点
MSVC 2017还内联了memcmp
以获得恒定的4字节大小,以及std::array
operator==,生成与gcc/clang相同的asm。(我没有测试早期版本)。参见纯C++版本。< /P>
从字符数组加载dword所需的语法是
dword ptr
size覆盖
// 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
}
作为一个完整的函数,此函数编译如下:
;; 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
第一条2mov
指令由编译器生成,用dword mov immediate将4字节数组存储到堆栈中
如果要返回0/non-0int
而不是0/1bool
,可以使用@p_uuuj_uuu的mov
/sub
建议,而不是在cmp
后检查标志。两个相等的DWORD将离开寄存器0,其他任何东西都不会。(xor
具有相同的属性。)
如果您想比较作为函数arg得到的
char*
的4个字节,它将是指针,而不是C数组,因此您必须自己在内联asm中将指针加载到寄存器中。(即使编译器在寄存器中已经有指针;MSVC内联asm语法基本上不适合小数据块,因为它强制执行存储/重新加载往返(大约5个延迟周期)对于输入和输出,除非您可以使用明显受支持的技巧,即在EAX中保留某些内容,并从非void函数的末尾掉下来。另请参阅与GNU C内联asm的比较,它可以轻松地请求寄存器中的输入并在寄存器中生成多个输出,从而允许编译器尽可能地优化当然,它仍然会阻止常量传播;如果使用memcmp
编译器可能只返回0
,因为数组具有编译时常量内容。)
无论如何,这是比较函数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
或其他工具进行编译,以启用在寄存器中传递参数的更好的调用约定,则实际情况会更糟:编译器必须将指针参数溢出到堆栈,以便asm重新加载它们,而不是将其变为reg移动。好了,没有办法通过强制转换或其他方式让编译器为您将指针加载到寄存器中,这样您就可以直接在内联asm中引用数组内容。您有大小为3的数组,但正在为它们分配4个对象。memcmp
呢?为什么要汇编?标准库函数比您可以编写的任何程序集都更高效。我敢打赌memcmp
通常会比您编写的任何程序集做得更好。要扩展@4386427的评论,这有一种非常强烈的气味,即@RezaBehboodi在assembley中编写并不保证高速。事实上,这意味着你正在以你“你”知道的速度前进。编译器在优化速度方面可能更好。因此,如果您正确地使用高级构造,您将以“编译器”知道的速度运行(在大多数情况下,这可能会击败“您”(当它没有击败您时,您将与您相等)。这就是dgbuff在提到过早优化时所说的。谢谢你的回答。我想要的正是这个解决方案assembly@RezaBehboodi又一次为什么要集会?学校的一项作业?如果是这样的话,那就去吧
int main()
{
volatile char b[4],a[4];
b[0]='A';b[1]='T';b[2]='C';b[3]='G';
a[0]='A';a[1]='T';a[2]='C';a[3]='G';
uint32_t val;
__asm__("movl %0, %%eax;" : "=m" (a) : "m" (a));
__asm__ ( "subl %1, %%eax;" : "=a" (val) : "m" (b) );
printf("%s\n", !val ? "Equal" : "Not equal");
}
# 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
// 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
}
;; 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
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
int main()
{
volatile char b[4],a[4];
b[0]='A';b[1]='T';b[2]='C';b[3]='G';
a[0]='A';a[1]='T';a[2]='C';a[3]='G';
uint32_t val;
__asm__("movl %0, %%eax;" : "=m" (a) : "m" (a));
__asm__ ( "subl %1, %%eax;" : "=a" (val) : "m" (b) );
printf("%s\n", !val ? "Equal" : "Not equal");
}