汇编中的 Scanf 和 printf 函数,关于 char* 和 double 的示例
Scanf and printf functions in assembly, example on char* and double
我有一个任务要做,使用 char* 和 double 变量调用 scanf 和 printf 函数。 Char* 正在工作,但我对 double 有疑问。
函数:用于 scanf/printf char*,函数 1:用于 scanf/printf 双精度。比如我编译后的结果:
(scanf)b
(printf)字符: b
(scanf)1.3
(printf)双:
99997200381866062965879955188785948733402760577162787362451212786.000000
问题似乎出在双变量的 printf 上,但我不知道如何解决。
.data
STDIN = 0
STDOUT = 1
SYSREAD = 3
SYSWRITE = 4
SYSEXIT = 1
EXIT_SUCCESS = 0
format_inchar: .string "%c"
format_indouble: .string "%lf"
char: .ascii " "
double: .double 0.0
format_string1: .string "char: %c\n"
format_double1: .string "double: %f\n"
.text
.global main
main:
function:
push $char
push $format_inchar
call scanf
push char
push $format_string1
call printf
function1:
push $double
push $format_indouble
call scanf
push double
push $format_double1
call printf
exit:
movl $SYSEXIT, %eax
movl $EXIT_SUCCESS, %ebx
int [=10=]x80
32 位代码无法通过一条指令从内存中 push
64 位 double
。 64 位代码不会在堆栈上传递参数,因此我假设这是 32 位代码。 (也来自使用 int 0x80
。)
push double
在32位代码中是pushl
压低4字节。 printf
从堆栈上它上面的任何内容中获取高 4 字节(包含指数和尾数的 most-significant 位)。在那种情况下,假设 scanf
没有破坏其堆栈参数,您的 double
bit-pattern 的高 4 字节来自地址 $format_indouble
.
你可能想要 pushl double+4
; pushl double
将 8 字节 double
分成两半。
(operand-size 后缀在这里是可选的,但我建议它避免让您产生歧义。)
或者您可以使用 SSE2 movsd
将 8 个字节复制到堆栈。
sub , %esp
movsd double, %xmm0
movsd %xmm0, (%esp)
(push
比较特殊,可以从内存复制到内存。具有显式源和目标的指令不能这样做。)
当然你的输入不需要静态存储space;您可以将指向堆栈内存的指针传递给 scanf
。然后你的 double
已经在堆栈上了,你可以只存储一个指向它下面的格式字符串的指针。
顺便说一句,调用 scanf
和 printf
时,堆栈没有按 16 对齐。 ABI 确实需要这个,所以你很幸运,你的 glibc 构建恰好没有在这些函数的堆栈内存上使用 movaps
,这会导致段错误。
在调用main
之前,堆栈按16对齐。call
压入一个return地址。因此,还有 3 个推 re-aligns 那个堆栈。函数条目上的一个虚拟推送将为您设置具有 8 个字节 args 的函数。
通常你会 add , %esp
在调用 return 之后从堆栈中清除参数。
变量名: double
是 C 中的类型名称(和关键字)。这使得它对于调用 C 函数的汇编程序来说是一个奇怪的名称。 dbl
之类的内容可能看起来更好。
我有一个任务要做,使用 char* 和 double 变量调用 scanf 和 printf 函数。 Char* 正在工作,但我对 double 有疑问。
函数:用于 scanf/printf char*,函数 1:用于 scanf/printf 双精度。比如我编译后的结果:
(scanf)b
(printf)字符: b
(scanf)1.3
(printf)双: 99997200381866062965879955188785948733402760577162787362451212786.000000
问题似乎出在双变量的 printf 上,但我不知道如何解决。
.data
STDIN = 0
STDOUT = 1
SYSREAD = 3
SYSWRITE = 4
SYSEXIT = 1
EXIT_SUCCESS = 0
format_inchar: .string "%c"
format_indouble: .string "%lf"
char: .ascii " "
double: .double 0.0
format_string1: .string "char: %c\n"
format_double1: .string "double: %f\n"
.text
.global main
main:
function:
push $char
push $format_inchar
call scanf
push char
push $format_string1
call printf
function1:
push $double
push $format_indouble
call scanf
push double
push $format_double1
call printf
exit:
movl $SYSEXIT, %eax
movl $EXIT_SUCCESS, %ebx
int [=10=]x80
32 位代码无法通过一条指令从内存中 push
64 位 double
。 64 位代码不会在堆栈上传递参数,因此我假设这是 32 位代码。 (也来自使用 int 0x80
。)
push double
在32位代码中是pushl
压低4字节。 printf
从堆栈上它上面的任何内容中获取高 4 字节(包含指数和尾数的 most-significant 位)。在那种情况下,假设 scanf
没有破坏其堆栈参数,您的 double
bit-pattern 的高 4 字节来自地址 $format_indouble
.
你可能想要 pushl double+4
; pushl double
将 8 字节 double
分成两半。
(operand-size 后缀在这里是可选的,但我建议它避免让您产生歧义。)
或者您可以使用 SSE2 movsd
将 8 个字节复制到堆栈。
sub , %esp
movsd double, %xmm0
movsd %xmm0, (%esp)
(push
比较特殊,可以从内存复制到内存。具有显式源和目标的指令不能这样做。)
当然你的输入不需要静态存储space;您可以将指向堆栈内存的指针传递给 scanf
。然后你的 double
已经在堆栈上了,你可以只存储一个指向它下面的格式字符串的指针。
顺便说一句,调用 scanf
和 printf
时,堆栈没有按 16 对齐。 ABI 确实需要这个,所以你很幸运,你的 glibc 构建恰好没有在这些函数的堆栈内存上使用 movaps
,这会导致段错误。
在调用main
之前,堆栈按16对齐。call
压入一个return地址。因此,还有 3 个推 re-aligns 那个堆栈。函数条目上的一个虚拟推送将为您设置具有 8 个字节 args 的函数。
通常你会 add , %esp
在调用 return 之后从堆栈中清除参数。
变量名: double
是 C 中的类型名称(和关键字)。这使得它对于调用 C 函数的汇编程序来说是一个奇怪的名称。 dbl
之类的内容可能看起来更好。