2012-11-30 21 views
2

我想写在大会下面的C代码:大会(AT&T 32位)scanf函数的问题

int main(void) 
{ 
    int x,y; 
    scanf("%d%d",&x,&y); 
    printf("%d%d",x,y); 
    return 0; 
} 

首先我只有一个整数尝试了扫描/打印:

.section .rodata #read only data section 
fmt: .string "%d%d" 
    .text 
.globl main  
    .type main, @function 
main: 
pushl %ebp #save the old frame pointer 
movl %esp, %ebp #create the new frame pointer 

pushl %esp #location of x 
pushl $fmt 
call scanf 

    #stack should now have exactly the scanned number x and then the format, as needed for printf. 
call printf 

movl $0, %eax 
movl %ebp, %esp #restore the old stack pointer - release all used memory. 
popl %ebp #restore old frame pointer (the caller function frame) 
ret 

但它不起作用。出于某种原因,下面的技巧使它工作(中的printf前加入):

addl $4,%esp #pop format 
pushl 4(%esp) 
pushl $fmt 

我不明白为什么会pushl 4(ESP%),使其工作,所以我的第一个问题,我要求关于这个问题的澄清。 然后我试图做同样的两个变量:

fmt: .string "%d%d" 
[...] 
    pushl %esp #location of x 
    pushl %esp #location of y 
    pushl $fmt 
    call scanf 

但它引起了分割错误。它甚至没有达到printf的一部分,在那里我会尝试这样的事情:前

addl $4,%esp #pop format  
pushl 8(%esp) 
pushl 8(%esp) 
pushl $fmt 

call printf 

(按照相同的逻辑,与pushl 4(%ESP) 所以我的第二个问题是如何能我让两个变量工作 谢谢

编辑:!?为什么下面的代码将无法扫描工作两个变量

subl $8,%esp #leave place for two vars 
pushl -4(%ebp) #location of x 
pushl -8(%ebp) #location of y 
pushl $fmt 
call scanf 

回答

1

“应该尤指4减去%,然后ESP的位置保存%ESP 指向”

这将发生在8086 CPU上。从80286,它内部存储ESP,然后减去,然后写入内部存储的值。您必须首先分配变量(一次推送或一次esp-4),然后存储此变量的地址(第二次推送)。对于第一个问题,你必须做3推或1次和2推。在你的情况下,esp指向存储旧EBP的堆栈位置。 您可以使用

push eax 
push esp 
push fmt 

这也可以。

此外,关于第二个问题,你提到了没有 甚至事情的线条,

噢,我复制错误的代码行,对不起。我对此作了回应:

pushl %esp #location of x 
pushl %esp #location of y 
pushl $fmt 
call scanf 

我指出,为什么你的代码不正确。您必须推送2个变量地址。相反,你推送旧的EBP的地址,然后用prev参数(指向旧的EBP)堆栈中的单元地址;结果,读取一个参数会搞砸,接收您在scanf上输入的值。当它想要写入另一个值时,而不是单元格的地址,它具有以前的int。

最后,你能解释你的建议代码吗?为什么我会提出EDX 和EAX到ESP,我甚至不知道什么是在他们

对不起,这是英特尔的语法,因此,MOV EAX,ESP的意思是“ESP写EAX”。 这确实不是一个很好的代码。只是举个例子。

我在栈上分配一个变量,得到它在eax中的地址。然后分配另一个var,将其地址存储在edx中。然后推送这两个地址,然后推动fmt的偏移量。

您必须先分配空间。除非您打算使用EBP相对地址解决本地变量,否则您不需要框架。 你可以推ebp - 4等 只需编译你的代码,看看它是如何工作在任何调试器(我用ollydbg检查你的代码); 最后,您可以要求C编译器生成asm列表并查看编译器如何执行此操作。

+0

现在我明白了一切,除了为什么不得不打扰推动%eax或什么。为什么不简单地在esps之前加-4(例如,使用代码pushl -4(%esp)#x的位置(并且在scanf之前对y是相同的)执行该操作? – nodwj

+0

因为它不会“执行技巧'如果你推esp - 4,你仍然不会为变量分配内存 –

+0

那么,如果我现在理解了这个问题,如果给scanf的地址指向自己,那么scanf不能覆盖这个地址? – nodwj

0

有了这个:

pushl %esp #location of x 
pushl $fmt 
call scanf 

您覆盖EBP。

首先,CPU记住寄存器的值(旧的esp值),然后减去4,然后保存旧的ESP值。在这种情况下,这是旧的EBP。当你第一次减去4时,你在堆栈上分配一个变量(最好是PUSH EAX--它比较短,只有1个字节);

类似的问题,第二种情况:

addl $4,%esp #pop format  
pushl 8(%esp) 
pushl 8(%esp) 
pushl $fmt 

这里第一个参数点不是X,而是第二个参数。第二点指向EBP。 你必须先在堆栈分配变量:

push ebp 
mov ebp, esp 
push eax 
mov edx, esp 
push eax 
mov eax, esp 
push eax 
push edx 
push offset fmt 
call xyz 

甚至更​​多:如果你不与本地变量的工作,你不需要推EBP,在建立帧指针没有必要。另外,在堆分配瓦尔后,您可以使用此:

LEA eax, [EBP - 4] 
LEA edx, [EBP - 8] 
+0

我不明白,为什么pushl%esp会覆盖%ebp?它应该用4减去%esp,然后在esp指向的位置保存%esp。另外,关于第二个问题,您提到的线路甚至都不重要,因为分段错误甚至更早触发,所以这不是问题。最后,你能解释你的建议代码吗?为什么我会将edx和eax移入esp,我甚至不知道它们是什么......谢谢! – nodwj

+0

作为另一个答案发布,因为它比允许的评论大。 –