AT&T 汇编-2: 使用C标准库IO函数 (2)

下面使用 c 标准库的函数 scanf() 来从标准输入读取数值,并使用 printf() 输出读到的值。

# cio-2.s
# using c function printf() and scanf()

.section .rodata
prompt:
   .asciz "enter an integer: "
output:
   .asciz "your input is %d.\n"
format:
   .asciz "%d"

.section .data
num:
   .int 0

.section .text
.globl main
main:
   pushl $prompt
   call  printf
   addl  $4, %esp

   pushl $num
   pushl $format
   call  scanf
   addl  $8, %esp

   pushl num
   pushl $output
   call  printf
   addl  $8, %esp

   pushl $0
   call  exit

第 4-10 行定义了一些字符串常量。

12-14 行:

.section .data
num:
   .int 0

定义了一个变量 num,它的类型是整型,初始化值为 0。.int 与 .asciz 等相似,用于定义数据的类型,一般情况下 .int 的大小是 4 个字节。除了这里出现的 .asciz,.int 和前面提到过的 .ascii 外,还有 .byte,.float 等标识符可以用来定义不同的数据类型。

num 位于 .data 段,与 .rodata 不同的是,.data 段的内容可以被修改。

第 19,20 行输出一段提示信息。第 21 行:

addl  $4, %esp

加法指令 addl 把 esp 的值加 4。add 系列指令的一般格式是:

add source, destination

计算 destination + source 的值,并把结果保存在 destination 中。与 mov 系列指令类似,我们在 add 后面加上不同的字母表示操作数的宽度,例如 addl 表示两个 32 位的数相加,而 addw 则表示两个 16 位的数相加,等等。

上半部分 中提到用完栈后应该及时恢复使用前的 esp 的值。这里我们只想恢复 esp 的值,并不关心上次调用的时候压入栈的值是什么,所以我们只需要把 esp 的值增加 4(上半部分 提到在 x86 上栈的增长方式是从高地址向低地址增长的),因为在调用 printf() 前我们为把 prompt 的 4 字节地址压入栈中。这条语句对于 esp 来说和上一篇提到的“popl %eax”是等价的,都是把 esp 的值加 4:

             +----+                               +----+
             | .. |                               | .. |
             +----+                               +----+
             | 10 | esp after 'addl %4, %esp' --> | 10 |
             +----+                               +----+
             | 12 |                               | 12 |
             +----+                               +----+
             | 34 |                               | 34 |
             +----+                               +----+
    ^        | 56 |                               | 56 |
    |        +----+                               +----+
  memory     | 78 | <-- esp after 'pushl $prompt' | 78 |
 addresses   +----+                               +----+

接下来为 scanf() 准备参数。语句

pushl $num
pushl $format

先后把 num 和 format 的地址压入栈中。在 c 语言中 scanf() 应该是这样的:

scanf("%d", &num);

format 和 &num 分别是 scanf() 的第一和第二个参数。c 语言函数参数的入栈顺序是相反的,也就是说第一个参数最后入栈,最后一个参数第一个入栈(想想 scanf() 的不定参数是怎样实现的?)。

然后调用 scanf(),运行完之后再恢复 esp 的值。因为这里入栈的是两个 32 位的地址值,所以 esp 加 8 而不是加 4。

第 28-30 行调用 printf() 输出 scanf() 读入的值:

pushl num
pushl $output
call  printf

这里的第一条语句把 num 的值而不是 num 的地址($num)压入栈中,因为 printf() 的调用应该是这样的:

printf("your input is %d\n", num);

最后程序的运行情况:

enter an integer: 5
your input is 5
发表于 2011年4月24日
本文目前尚无任何评论.

发表评论

XHTML: 您可以使用这些标签: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>