汇编中的 Scanf 和 printf 函数,例如 char* 和 double
Posted
技术标签:
【中文标题】汇编中的 Scanf 和 printf 函数,例如 char* 和 double【英文标题】:Scanf and printf functions in assembly, example on char* and double 【发布时间】:2019-10-25 06:58:36 【问题描述】:我有一个任务要做,使用 char* 和 double 变量调用 scanf 和 printf 函数。 Char* 正在工作,但我有双重问题。
Function:用于 scanf/printf char*,function1:用于 scanf/printf double。比如我编译后的结果:
(scanf)b
(printf)char: 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 $0x80
【问题讨论】:
【参考方案1】:32 位代码不能用一条指令从内存中 push
64 位 double
。 64 位代码不会在堆栈上传递参数,所以我认为这是 32 位代码。 (也来自使用int 0x80
。)
push double
在 32 位代码中是 pushl
,它推动低 4 个字节。 printf
正在从堆栈上方的任何内容中获取高 4 个字节(包含尾数的指数和最高有效位)。在这种情况下,假设scanf
没有破坏它的堆栈参数,您的double
位模式的高4 字节来自地址$format_indouble
。
你可能想要pushl double+4
; pushl double
将 8 字节的 double
分成两半。
(操作数大小的后缀在这里是可选的,但我建议使用它以避免让您产生歧义。)
或者您可以使用 SSE2 movsd
将 8 个字节复制到堆栈。
sub $8, %esp
movsd double, %xmm0
movsd %xmm0, (%esp)
(push
是特殊的,可以从内存复制到内存。具有显式源和目标的指令不能这样做。)
当然,您的输入不需要静态存储空间;您可以将指向堆栈内存的指针传递给scanf
。那么你的double
就已经在堆栈上了,你可以只在它下面存储一个指向格式字符串的指针。
顺便说一句,当您调用 scanf
和 printf
时,您没有将堆栈对齐为 16。 ABI 确实需要这个,所以你很幸运,你的 glibc 构建碰巧没有在这些函数的堆栈内存上使用movaps
,这会出现段错误。
在调用main
之前,堆栈按16 对齐。call
推送返回地址。因此,再进行 3 次推送会重新对齐该堆栈。对函数入口的一次虚拟推送将为您设置具有 8 个字节 args 的函数。
通常你会在调用返回后add $8, %esp
清除堆栈中的参数。
变量名称: double
是 C 中的类型名称(和关键字)。这使得它对于调用 C 函数的汇编程序来说是一个奇怪的名称。像dbl
这样的东西可能看起来更好。
【讨论】:
你成功了(惊喜),但值得提醒的是,ABI 是由架构 + 平台定义的。您描述了 sysv/386 abi;但某些 windows 变体可能会有所不同。 @mevets:使用 Windowsfastcall
OP 的 char
版本也无法正常工作。除了 Windows 不使用 32 位 fastcall
用于可变参数函数,因为它是被调用者弹出约定; IIRC i386 Windows printf/scanf 使用 cdecl 调用约定,在这种情况下与 i386 System V 匹配。以上是关于汇编中的 Scanf 和 printf 函数,例如 char* 和 double的主要内容,如果未能解决你的问题,请参考以下文章