Visual c++ 在Microsoft Visual Studio中读取并显示程序集中的浮点数或双倍数
我正在使用Microsoft Visual Studio 2015学习内联汇编编程。我读过很多关于stackoverflow的帖子,包括最相关的一篇,但是在我尝试了这些方法之后,结果仍然是0.0000。我首先使用float并将值存储到fpu,但reuslt是相同的,并尝试将值传递给eax,但仍然没有帮助 这是我的密码:Visual c++ 在Microsoft Visual Studio中读取并显示程序集中的浮点数或双倍数,visual-c++,assembly,floating-point,x86,masm,Visual C++,Assembly,Floating Point,X86,Masm,我正在使用Microsoft Visual Studio 2015学习内联汇编编程。我读过很多关于stackoverflow的帖子,包括最相关的一篇,但是在我尝试了这些方法之后,结果仍然是0.0000。我首先使用float并将值存储到fpu,但reuslt是相同的,并尝试将值传递给eax,但仍然没有帮助 这是我的密码: #include "stdafx.h" int _tmain(int argc, _TCHAR* argv[]) { char input1[] = "Enter n
#include "stdafx.h"
int _tmain(int argc, _TCHAR* argv[])
{
char input1[] = "Enter number: \n";
char input_format[] = "%lf";
double afloat;
char output[] = "The number is: %lf";
_asm {
lea eax, input1
push eax
call printf_s
add esp, 4
lea eax, input_format
push eax
lea eax, afloat
push eax
call scanf_s
add esp, 8
sub esp, 8
fstp [esp]
lea eax, output
push eax
call printf
add esp, 12
}
return 0;
}
结果:您是否使用调试器查看寄存器/内存
fstp[esp]
非常可疑,因为此时ST0应该是空的。scanf返回一个整数,调用约定要求x87堆栈在调用/返回时为空,FP返回值除外
我忘记了当x87堆栈为空时进行FST时会发生什么。如果你得到零,这就可以解释了,因为这就是你要传递给printf的内容
添加esp,8
/子esp,8
是完全冗余的。那样做没有意义。你可以把它拿出来。(或者对其进行注释,但将其保留在那里,以提醒您已通过重用堆栈上的arg空间进行了优化,而不是将其弹出并推送新内容。)
由于
scanf
在您传递的地址写入double
,您可以通过让它在堆栈底部附近写入来避免复制它,然后将ESP调整到它的正下方。按下一个格式字符串,就可以调用printf了,double
已经作为第二个参数出现在堆栈上
sub esp, 8 ; reserve 8 bytes for storing a `double` on the stack
push esp ; push a pointer to the bottom of that 16B we just reserved
push OFFSET input_format ; (only works if it's in static storage, but it's a string constant so you should have done that instead of initializing a non-const array with a literal inside `main`.)
call scanf ; scanf("%lf", ESP_before_we_pushed_args)
add esp, 8
; TODO: check return value and handle error
; esp now points at the double stored by scanf
push OFFSET output_format
call printf
add esp, 12 ; "pop" the args: format string and the double we made space for before the scanf call.
如果您使用的调用约定/ABI要求您使用ESP 16B对齐进行函数调用,那么您不能将其缩短这么多:您需要一个lea eax、[ESP+4]
/push eax
或其他替代push ESP
的东西,因为double
不能正好位于scanf的第二个参数之上,但如果两个调用都对齐了堆栈16B,则它也可以是printf的第二个参数。因此,让scanf将双精度
存储在更高的地址,并添加esp,12
以访问它
IDK如果MSVC风格的内联asm首先保证任何类型的堆栈对齐。内联asm在某些方面似乎比在asm中编写整个函数更复杂。您是否使用调试器查看寄存器/内存
fstp[esp]
非常可疑,因为此时ST0应该是空的。scanf返回一个整数,调用约定要求x87堆栈在调用/返回时为空,FP返回值除外
我忘记了当x87堆栈为空时进行FST时会发生什么。如果你得到零,这就可以解释了,因为这就是你要传递给printf的内容
添加esp,8
/子esp,8
是完全冗余的。那样做没有意义。你可以把它拿出来。(或者对其进行注释,但将其保留在那里,以提醒您已通过重用堆栈上的arg空间进行了优化,而不是将其弹出并推送新内容。)
由于
scanf
在您传递的地址写入double
,您可以通过让它在堆栈底部附近写入来避免复制它,然后将ESP调整到它的正下方。按下一个格式字符串,就可以调用printf了,double
已经作为第二个参数出现在堆栈上
sub esp, 8 ; reserve 8 bytes for storing a `double` on the stack
push esp ; push a pointer to the bottom of that 16B we just reserved
push OFFSET input_format ; (only works if it's in static storage, but it's a string constant so you should have done that instead of initializing a non-const array with a literal inside `main`.)
call scanf ; scanf("%lf", ESP_before_we_pushed_args)
add esp, 8
; TODO: check return value and handle error
; esp now points at the double stored by scanf
push OFFSET output_format
call printf
add esp, 12 ; "pop" the args: format string and the double we made space for before the scanf call.
如果您使用的调用约定/ABI要求您使用ESP 16B对齐进行函数调用,那么您不能将其缩短这么多:您需要一个lea eax、[ESP+4]
/push eax
或其他替代push ESP
的东西,因为double
不能正好位于scanf的第二个参数之上,但如果两个调用都对齐了堆栈16B,则它也可以是printf的第二个参数。因此,让scanf将双精度
存储在更高的地址,并添加esp,12
以访问它
IDK如果MSVC风格的内联asm首先保证任何类型的堆栈对齐。内联asm在某些方面似乎比在asm中编写整个函数更复杂。您试图打印错误的值。事实上,代码应该只是导致无意义的内容被打印到终端上。您很幸运看到
0.0
。让我们具体看一下检索浮点值的代码部分,这是对scanf\u s
的调用:
leaeax,输入\格式
推送eax
lea eax,漂浮
推送eax
打电话给scanf_s
添加esp,8
副esp,8
fstp[esp]
首先,我看不出在堆栈指针(esp
)中添加8,然后立即从堆栈指针中减去8有什么逻辑。背对背地执行这两个操作只是相互抵消。因此,可以删除这两条说明
其次,将参数按错误的顺序推送到堆栈上。cdecl调用约定(CRT函数使用,包括printf_s
和scanf_s
)按从右到左的相反顺序传递参数。因此,要调用scanf_s
,首先要推送输入应该存储到的浮点值的地址,然后推送格式控制字符串缓冲区的地址。您的代码是错误的,因为它将参数从左向右推。使用printf\u s
,您会很幸运,因为您只传递了一个参数,但因为您将两个参数传递给scanf\u s
,所以会发生不好的事情
第三个问题是,您似乎假设scanf_s
直接返回输出
如果它返回了,并且您请求了一个浮点值,那么cdecl
调用约定会让它在FP(0)
中的浮点堆栈顶部返回该值,这样您就可以正确地弹出该值和