C语言到汇编-变量
变量,类似于汇编中的标号,例如:
data segment
year dw 0
data ends
这段代码中的year 就是一个标号,它实际上指的是一个长度为2个字节(dw)的内存空间的地址,它的初始值是0。在C语言中,定义变量的格式是:
int year = 0;
这段代码的意思是定义了一个int 类型的变量,变量名为year ,初始值为0。int 类型的取值范围取决于具体的机器。通常是16位,也就是2个字节,也有用32位表示的。
以上两种定义year 初始值的二进制值皆为“00000000 00000000”。(假设int 为16位)
定义变量后,就可以为变量赋予不同的值了,比如:
year = 1998;
对应的汇编语言:
mov year, 1998
C语言的基本数据类型有:
int 整数型
float 浮点型
char 字符型(一个字节)
short 短整型
long 长整型
double 双精度浮点型
C语言有关变量的一些细节知识与其他语言(如Java)并没有什么不同,不再细说。
看一个C语言变量的示例代码:
int year = 1998;
main()
{
int x = 7;
int y = 9;
float z = 3.5;
z = x + y;
}
将这段代码保存在var.c 文件中,并用gcc 指令将其编译为汇编代码:
gcc -S var.c -masm=intel
得到汇编代码为:
.file "var.c"
.intel_syntax
.globl _year
.data
.align 4
_year:
.long 1998
.def ___main; .scl 2; .type 32; .endef
.text
.globl _main
.def _main; .scl 2; .type 32; .endef
_main:
push ebp
mov ebp, esp
sub esp, 24
and esp, -16
mov eax, 0
add eax, 15
add eax, 15
shr eax, 4
sal eax, 4
mov DWORD PTR [ebp-16], eax
mov eax, DWORD PTR [ebp-16]
call __alloca
call ___main
mov DWORD PTR [ebp-4], 7
mov DWORD PTR [ebp-8], 9
mov eax, 0x40600000
mov DWORD PTR [ebp-12], eax
mov eax, DWORD PTR [ebp-8]
add eax, DWORD PTR [ebp-4]
push eax
fild DWORD PTR [esp]
lea esp, [esp+4]
fstp DWORD PTR [ebp-12]
leave
ret
去掉不关心的内容后:
.globl _year
.data //.data通知as汇编后续语句,将它们追加在编号为0的数据段末。
.align 4 //增加位置计数器(在当前的子段)使它指向规定的存储边界。
_year:
.long 1998 //.long expressions 每个expressions都生成一个数字,这个数字等于表达式在目标机器运行时的值。等价于.int。
_main:
mov DWORD PTR [ebp-4], 7
mov DWORD PTR [ebp-8], 9
mov eax, 0x40600000
mov DWORD PTR [ebp-12], eax
mov eax, DWORD PTR [ebp-8]
add eax, DWORD PTR [ebp-4]
push eax
fild DWORD PTR [esp]
lea esp, [esp+4]
fstp DWORD PTR [ebp-12]
leave
ret
.data 是另一种定义数据段的方式,等价于:
data segment
data ends
类似的还有.code,.stack 等。(参考《Intel汇编语言设计 第五版》 3.18节 伪指令。王爽的《汇编语言》只是入门,很多内容都没讲,所以找了本更全面的书当做参考工具书。)
可以看到汇编代码中全局变量year 的定义,除了一些细节,基本与文章开头处的定义方式一致:
data segment
year dw 0
data ends
.globl _year
.data
_year:
.long 1998
再看其他代码的对比:
int x = 7;
int y = 9;
float z = 3.5;
z = x + y;
mov DWORD PTR [ebp-4], 7
mov DWORD PTR [ebp-8], 9
mov eax, 0x40600000
mov DWORD PTR [ebp-12], eax
mov eax, DWORD PTR [ebp-8]
add eax, DWORD PTR [ebp-4]
push eax
fild DWORD PTR [esp]
lea esp, [esp+4]
fstp DWORD PTR [ebp-12]
通过对比可以明显的看出,3个变量的值被依次压入了类似栈结构的内存空间中,每个值占4个字节,即32位。(前4行)
接着将变量x 和y 的值取出来进行相加,再将结果压入栈中。(5-7行)
最后将整数型结果转为浮点型。(8-10行)
几个新汇编指令:
fild指令:把整数转换成浮点数并加载至寄存器栈。
lea指令:计算并装入16位或32位的内存操作数的有效地址。LEA指令获取的地址是在运行时进行计算的。
fstp指令:存储浮点值并出栈。
最后一行指令,将转换完的结果从栈中弹出存入[ebp-12] 内存单元:
fstp DWORD PTR [ebp-12]
而根据前面4、5两行代码可以看出,[ebp-12] 内存单元存放的即是变量z 的值:
mov eax, 0x40600000 //浮点数3.5的值
mov DWORD PTR [ebp-12], eax
好了,关于变量就先学到这里,下一篇开始学习C语言的控制流。