汇编语言中使用scanf获取双字符存入栈并打印的问题排查
问题
编写汇编程序时碰到这么个情况:
我要拿两个数字(按字符处理)存进栈里,之后逐个取出来打印。用scanf配"%c %c"的格式控制符读输入,但第二个字符总是读到换行符。
现在我是每次用subq $8, %rsp给每个字符分配栈空间,把栈地址分别传到%rsi和%rdx后调用scanf,但打印的时候只能输出第二个数字和换行符。我觉得分配的空间够,传的地址也对,但就是存不对内容,想知道怎么用scanf把字符正确存进栈里。
现有代码片段:
movq $scanf_c, %rdi subq $8, %rsp movq %rsp, %rsi subq $8, %rsp movq %rsp, %rdx call scanf
解决方案
问题根因
你现在的问题主要出在两个地方:
- 栈地址传递顺序搞反了:栈是向下增长的,第一次
subq $8, %rsp得到的地址比第二次的要高(更靠近栈顶),但scanf的参数顺序是%rsi对应格式串里第一个%c,%rdx对应第二个%c,你现在把低地址传给了第二个%c,等于把两个字符的存储位置弄反了。 - 虽然单个字符只需要1字节,你分配8字节完全没问题,但后续打印如果没正确取单个字节,也会输出垃圾数据。
修改后的代码示例
; 一次性分配16字节栈空间(满足栈对齐要求,同时存两个字符) subq $16, %rsp ; 第一个%c的存储地址:当前rsp(较高地址),传给rsi movq %rsp, %rsi ; 第二个%c的存储地址:rsp+1(连续的下一个字节),传给rdx leaq 1(%rsp), %rdx ; 加载格式串地址到rdi movq $scanf_c, %rdi call scanf ; 打印测试(假设printf_c是"%c %c\n") movq $printf_c, %rdi ; 取出第一个字符(用movb读单个字节) movb (%rsp), %sil ; 取出第二个字符 movb 1(%rsp), %dl call printf ; 释放栈空间 addq $16, %rsp
关键注意点
- 栈是向下增长的,所以先分配的地址对应第一个输入字符,第二个字符直接存在下一个字节就行,没必要每次减8浪费空间。
- 如果非要坚持每次减8的方式,那得把第一次减8后的地址传给
%rdx,第二次减8后的传给%rsi,但这种方式会浪费栈空间,不推荐。 - 打印的时候一定要用
movb读取单个字节,别用movq,不然会把后面的7字节垃圾数据也读进去,导致输出异常。
内容的提问来源于stack exchange,提问作者TBY
相关产品推荐
相关产品推荐

