C++ 将引用参数传递给汇编函数
我有一个包含3个引用参数的函数,其中包含一些汇编代码。 我想得到变量R,G,B中函数的结果,如下所示C++ 将引用参数传递给汇编函数,c++,assembly,x86,C++,Assembly,X86,我有一个包含3个引用参数的函数,其中包含一些汇编代码。 我想得到变量R,G,B中函数的结果,如下所示 void Get_RGB_color(const DWORD &color, uint8_t &R, uint8_t & G, uint8_t & B) { _asm { push EAX; xor EAX, EAX; mov EAX,color; mov R, AL;
void Get_RGB_color(const DWORD &color, uint8_t &R, uint8_t & G, uint8_t & B) {
_asm {
push EAX;
xor EAX, EAX;
mov EAX,color;
mov R, AL;
mov G, AH;
shr EAX, 16;
mov B, AL;
pop EAX;
}
}
例如,我使用函数作为
DWORD color=RGB(76,0,0);
uint8_t R, G, B;
Get_RGB_color(color , R ,G ,B );
这些代码有两个问题:
1-在EAX中获取错误值
mov-EAX,颜色强>
2-行中出现错误“操作数大小冲突”
mov-R,AL;
mov G,啊,;
mov-B,AL
请帮帮我
你为什么把EAX推到这里?没有必要这样做。EAX是一个调用者保存寄存器,这意味着被调用者(即,您的函数)可以自由地对其进行删除。您无需保留其价值。(EAX、EDX和ECX是Win32 ABI中的调用方保存寄存器;其他是调用方保存寄存器。) 推送寄存器的唯一其他原因是对齐堆栈,但这在这里也不是必需的。当控件传递给函数时,堆栈将已正确对齐 我想您知道这是清除寄存器的常用技巧(用自身进行XORing)。但是,在将值移动到寄存器中之前,不需要预先清除寄存器 这条线错了;这就是汇编程序错误告诉你的
color
作为对DWORD的引用传递给此函数,但在引擎盖下,引用作为指针实现,因此它实际上作为指向DWORD的指针传递。这意味着您不能直接访问color的值,必须使用指针间接寻址(或x86术语)。由于您使用的是内联汇编,因此可以让编译器为您进行堆栈簿记,只需通过形式参数的名称引用内存位置:
mov EAX, DWORD PTR [color] ; get the address of color
mov EAX, DWORD PTR [EAX] ; dereference it, storing the result in EAX
当然,由于您实际上没有在该函数内部修改颜色
,因此没有理由将其作为参考参数传递。通常,标量值(例如整数)应始终通过值传递,而不是通过引用传递,除非您实际需要引用。优化编译器将在寄存器中传递值,这使得指针间接寻址及其伴随的开销完全不必要,因此效率更高,可读性更强。
这里,汇编器给您一个“操作数大小冲突”错误。因为R
实际上是一个引用,作为指针实现,所以它是32位的。它是指向内存中8位位置的32位指针。因此,您试图将8位值(AL)移动到32位位置(指针)。操作数的大小不同。所以再一次,你必须使用间接寻址。它看起来就像上面的代码,只是现在R
是字节大小的,您需要使用不同的寄存器作为暂存寄存器,以避免重击EAX中的值,我们为此付出了巨大努力:
mov EDX, DWORD PTR [R] ; get the address of R
mov BYTE PTR [EDX], AL ; dereference it so we can store AL in there
这将EAX的低位字节(我们可以称之为AL)移动到R
指定的字节大小的内存位置
下一行也是一样,只是现在您正在移动EAX的高位字节(称为AH)。我们现在可以在这里重用EDX,因为我们不再需要它的旧值:
mov EDX, DWORD PTR [G] ; get the address of G
mov BYTE PTR [EDX], AH ; dereference it so we can store AH in there
这是正确的
第三节,和第一节一样。如您所知,这应该是:
mov EDX, DWORD PTR [B] ; get the address of B
mov BYTE PTR [EDX], AL ; dereference it so we can store AL in there
弹出EAX现在是不必要的,因为我们在开始时没有推动EAX
然后,将所有这些放在一起,您将得到以下一系列说明:
void Get_RGB_color(const DWORD &color, uint8_t &R, uint8_t & G, uint8_t & B)
{
__asm
{
mov EAX, DWORD PTR [color]
mov EAX, DWORD PTR [EAX]
mov EDX, DWORD PTR [R]
mov BYTE PTR [EDX], AL
mov EDX, DWORD PTR [G]
mov BYTE PTR [EDX], AH
shr EAX, 16
mov EDX, DWORD PTR [B]
mov BYTE PTR [EDX], AL
}
}
然而,这并不是编写代码的最佳方式。访问32位寄存器的低8位和高8位虽然是允许的,但速度较慢。优化编译器可以避免这种情况,并且在此过程中,可以避免使用移位指令:
void Get_RGB_color(const DWORD &color, uint8_t &R, uint8_t & G, uint8_t & B)
{
__asm
{
mov EAX, DWORD PTR [color] ; get the address of color
mov EAX, DWORD PTR [EAX] ; get the value in EAX
mov EDX, DWORD PTR [R] ; get the address of R
mov CL, BYTE PTR [EAX] ; get the value of the lowest byte (8 bits) of color
mov BYTE PTR [EDX], CL ; dereference R and store that byte in it
mov EDX, DWORD PTR [G] ; get the address of G
mov CL, BYTE PTR [EAX + 1] ; get the value of the second-to-lowest byte in color
mov BYTE PTR [EDX], CL ; dereference G and store that byte in it
mov EDX, DWORD PTR [B] ; get the address of B
mov CL, BYTE PTR [EAX + 2] ; get the value of the third-to-lowest byte in color
mov BYTE PTR [EDX], CL ; dereference B and store that byte in it
}
}
但仍有部分登记摊位潜伏在那里,以减缓事态的发展。因此,一个真正聪明的编译器可以通过预调零寄存器或使用movzx
来消除这些错误:
void Get_RGB_color(const DWORD &color, uint8_t &R, uint8_t & G, uint8_t & B)
{
__asm
{
mov EAX, DWORD PTR [color]
mov EAX, DWORD PTR [EAX]
mov EDX, DWORD PTR [R]
movzx ECX, BYTE PTR [EAX]
mov BYTE PTR [EDX], CL
mov EDX, DWORD PTR [G]
movzx ECX, BYTE PTR [EAX + 1]
mov BYTE PTR [EDX], CL
mov EDX, DWORD PTR [B]
movzx ECX, BYTE PTR [EAX + 2]
mov BYTE PTR [EDX], CL
}
}
它还可以对指令进行重新排序,并巧妙地分配寄存器以尽可能地并行这三个操作。毫无疑问,还有更有效的方法可以做到这一点。除非您试图学习汇编语言编程(在这种情况下,使用内联汇编程序没有多大意义),否则强烈希望编写如下代码:
void Get_RGB_color(const DWORD &color, uint8_t &R, uint8_t & G, uint8_t & B)
{
R = (color & 0xFF);
G = ((color >> 8) & 0xFF);
B = ((color >> 16) & 0xFF);
}
最后一点注意:即使在使用内联汇编语法时,也不需要以分号结束每个汇编语言指令。这只是C和C++中的必要条件。不过,这并不重要,因为分号实际上是汇编程序中的注释分隔符,所以它类似于在C中编写以下代码:
int foo;/**/
引用实际上是作为指针传递的,您需要这样对待它们。PS:没有理由使用汇编。我想我可以得到变量的地址,如何才能得到变量的值?我要强调Jester已经说过的话。你认为这个程序集会比编译器生成的代码快吗?我认为程序集代码是获得结果的较短方式,速度对这个主题并不重要。在这种情况下,在程序集中编写代码既不短也不快。使用这样的简单代码,编译器可以做得更好。说到更快和更短,您最好通过值传递像
color
这样的简单标量类型,而不是通过引用。这样,这个值可以简单地登记在一个寄存器中。谢谢你的帮助。@赛义德7:如果这个答案帮助你解决了你的问题,那么你应该考虑接受答案。更多接受答案的方式和原因可在此处找到:。我还注意到你从未接受过任何对你有用的答案。你看起来是新来的,我建议你回顾一下你的其他问题,接受任何能解决你问题的答案。欢迎来到Stackoverflow!
mov EDX, DWORD PTR [B] ; get the address of B
mov BYTE PTR [EDX], AL ; dereference it so we can store AL in there
pop EAX;
void Get_RGB_color(const DWORD &color, uint8_t &R, uint8_t & G, uint8_t & B)
{
__asm
{
mov EAX, DWORD PTR [color]
mov EAX, DWORD PTR [EAX]
mov EDX, DWORD PTR [R]
mov BYTE PTR [EDX], AL
mov EDX, DWORD PTR [G]
mov BYTE PTR [EDX], AH
shr EAX, 16
mov EDX, DWORD PTR [B]
mov BYTE PTR [EDX], AL
}
}
void Get_RGB_color(const DWORD &color, uint8_t &R, uint8_t & G, uint8_t & B)
{
__asm
{
mov EAX, DWORD PTR [color] ; get the address of color
mov EAX, DWORD PTR [EAX] ; get the value in EAX
mov EDX, DWORD PTR [R] ; get the address of R
mov CL, BYTE PTR [EAX] ; get the value of the lowest byte (8 bits) of color
mov BYTE PTR [EDX], CL ; dereference R and store that byte in it
mov EDX, DWORD PTR [G] ; get the address of G
mov CL, BYTE PTR [EAX + 1] ; get the value of the second-to-lowest byte in color
mov BYTE PTR [EDX], CL ; dereference G and store that byte in it
mov EDX, DWORD PTR [B] ; get the address of B
mov CL, BYTE PTR [EAX + 2] ; get the value of the third-to-lowest byte in color
mov BYTE PTR [EDX], CL ; dereference B and store that byte in it
}
}
void Get_RGB_color(const DWORD &color, uint8_t &R, uint8_t & G, uint8_t & B)
{
__asm
{
mov EAX, DWORD PTR [color]
mov EAX, DWORD PTR [EAX]
mov EDX, DWORD PTR [R]
movzx ECX, BYTE PTR [EAX]
mov BYTE PTR [EDX], CL
mov EDX, DWORD PTR [G]
movzx ECX, BYTE PTR [EAX + 1]
mov BYTE PTR [EDX], CL
mov EDX, DWORD PTR [B]
movzx ECX, BYTE PTR [EAX + 2]
mov BYTE PTR [EDX], CL
}
}
void Get_RGB_color(const DWORD &color, uint8_t &R, uint8_t & G, uint8_t & B)
{
R = (color & 0xFF);
G = ((color >> 8) & 0xFF);
B = ((color >> 16) & 0xFF);
}
int foo;/**/