函数的内部处理及全局变量和局部变量

简介: 函数的内部处理及全局变量和局部变量

一、函数的内部处理


上一篇博客用汇编分析了 Sample4.c 整个代码过程,现在分析以下AddNum函数的源代码部分,分析一下参数的接收、返回值等机制:


_AddNum    proc    near
 push      ebp                   ---(1)
 mov       ebp,esp               ---(2)
 mov       eax,dword ptr[ebp+8]  ---(3)
 add       eax,dword ptr[ebp+12] ---(4)
 pop       ebp                   ---(5)
 ret                             ---(6)
_AddNum    endp


    ebp 寄存器的值在(1)中入栈,在(5)中出栈,这主要是为了把函数中用到的ebp 寄存器的内容,恢复到函数调用前的状态


       (2)中把负责管理栈地址的esp 寄存器的值赋值到了ebp 寄存器中。这是因为,在mov 指令中方括号内的参数,是不允许指定 esp寄存器的。因此,这里就采用了不直接通过esp,而是用ebp 寄存器来读写栈内容的方法


       (3)使用[ebp+8]指定栈中存储的第1个参数123,并将其读出到 eax 寄存器中。像这样,不使用pop指令,也可以参照栈的内容。而之所以从多个寄存器中选择了eax寄存器,是因为eax是负责运算的累加寄存器


       通过(4)的add指令,把当前eax 寄存器的值同第2个参数相加后的结果存储在eax寄存器中。[ebp+12]是用来指定第2个参数456的。在C语言中,函数的返回值必须通过eax 寄存器返回,这也是规定。也就是函数的参数是通过栈来传递,返回值是通过寄存器返回的


       (6)中ret指令运行后,函数返回目的地内存地址会 自动出栈,据此,程序流程就会跳转返回到(6)(Call_AddNum)的下一行。这时,AddNum函数入口和出口处栈的状态变化,就如下图所示


AddNum函数内部的栈状态变化:



二、全局变量和局部变量


在函数外部定义的变量称之为全局变量,在函数内部定义的变量称为局部变量,全局变量可以在任意函数中使用,局部变量只能在函数定义局部变量的内部使用。下面,通过汇编语言来看一下全局变量和局部变量的不同之处


下面定义的C语言代码分别定义了局部变量和全局变量,并且给各变量进行了赋值,代码:


// 定义被初始化的全局变量
int a1 = 1;
int a2 = 2;
int a3 = 3;
int a4 = 4;
int a5 = 5;
//定义没有初始化的全局变量
int b1,b2,b3,b4,b5;
//定义函数
void MyFunc(){
//定义局部变量
int c1,c2,c3,c4,c5,c6,c7,c8,c9,c10;
//给局部变量赋值
c1 = 1;
c2 = 2;
c3 = 3;
c4 = 4;
c5 = 5;
c6 = 6;
c7 = 7;
c8 = 8;
c9 = 9;
c10 = 10;
//把局部变量赋值给全局变量
a1 = c1;
a2 = c2;
a3 = c3;
a4 = c4;
a5 = c5;
b1 = c6;
b2 = c7;
b3 = c8;
b4 = c9;
b5 = c10;
}


用Borland C++ 编译后的汇编代码可以大致分为:


1、初始化的全局变量,会汇总到名为 _DATA 的段定义中

_DATA segment dword public use32 'DATA'
...
_DATA ends


2、没有初始化的全局变量,会汇总到名为 _BSS 的段定义中


_BSS segment dword public use32 'BSS'
...
_BSS ends


3、被段定义 _TEXT 围起来的汇编代码则是 Borland C++定义

_TEXT segment dword public use32 'CODE'
_MyFunc proc near
...
_MyFunc endp
_TEXT ends


汇编指令:


操作码 操作数 功能


add A,B

把A和B的值相加,并把结果赋值为A


call A 调用函数A


cmp A,B 对A和B进行比较,比较结果会自动存入标志寄存器中


inc A 对A的值 +1
ige 标签名 和cmp命令组合使用,跳转到标签行
jl 标签名 和cmp命令组合使用,跳转到标签行
jle 标签名 和cmp命令组合使用,跳转到标签行
jmp 标签名 和cmp命令组合使用,跳转到标签行
mov A,B 把B的值赋给A
pop A 从栈中读取数值并存入A
push A 把A的值存入栈中
ret 将处理返回到调用源
xor A,B A和B进行异或比较,并将结果存入A中

 

我们首先来看一下_DATA 段定义的内容。_a1 label dword 定义了_a1 这个标签。标签表示的是相对于段定义起始位置的位置。由于_a1 在_DATA 段定义的开头位置,所以相对位置是0。_a1 就相当于是全局变量a1。编译后的函数名和变量名前面会加一个(_),这也是Borland C++的规定。dd 1 指的是,申请分配了4字节的内存空间,存储着1这个初始值。dd指的是 define double word 表示有两个长度为2的字节领域(word),也就是4字节的意思


       Borland C++中,由于 int 类型的长度是4字节,因此汇编器就把int a1=1变换成了_a1 labeldword 和dd 1。同样,这里也定义了相当于全局变量的a2-a5的标签_a2-_a5,它们各自的初始值2-5也被存储在各自的4字节中


       接下来,我们来说一说_BSS段定义的内容。这里定义了相当于全局变量b1-b5的标签_b1-_b5。其中的 db 4dup(?)表示的是申请分配了4字节的领域,但值尚未确定(这里用?来表示)的意思。 db(define byte)表示有1个长度是1字节的内存空间。因而,db 4 dup(?)的情况下,就是4字节的内存空间


       注意:db 4 dup(?)不要和dd 4混淆了,前者表示的是4个长度是1字节的内存空间。而db4表示的则是双字节(=4字节)的内存空间中存储的值是4


目录
相关文章
|
1月前
初始化局部变量和全局变量
【10月更文挑战第3天】初始化局部变量和全局变量。
32 5
|
4月前
|
开发者
局部变量,在使用时再定义
关于局部变量,适时定义可以提高代码可读性并规避不必要的bug。示例代码中,为了避免误解`checkTaskApplyDTO`仅设置了`userId`,在`existAppliedTask`方法内部,可以通过将`checkTaskApplyDTO`的定义与设置属性的操作靠近,以明确其所有属性值的来源。 另外,本文还展示了一个因提前定义变量`ret`而导致的bug实例。如果将此变量的定义延迟至其实际使用前,则可以避免此类问题。适时定义变量有助于减少混淆,提高代码质量。
40 4
|
存储 C语言 Perl
西门子S7-1200的变量如何使用?什么是局部变量和全局变量?临时变量和静态变量有什么区别?
今天给大家讲一下什么是局部变量、全局变量、临时变量、静态变量,这些变量都有什么区别,以及在西门子S7-1200中这些变量如何来使用。
西门子S7-1200的变量如何使用?什么是局部变量和全局变量?临时变量和静态变量有什么区别?
|
C++
39.【C/C++ 全局变量和局部变量 (详解)】
39.【C/C++ 全局变量和局部变量 (详解)】
82 0
|
存储
全局变量和局部变量
全局变量和局部变量
75 0
|
C++
【C++】局部变量和全局变量
有关C++局部变量和全局变量的记录
130 0
【C++】局部变量和全局变量
|
编译器 C语言
局部变量和全局变量
一、局部变量 二、全局变量
|
JavaScript 前端开发
0307-全局变量和局部变量
全局变量: window对象下声明的变量 局部变量: function内声明的变量 不加var的时候,局部变量会变成全局变量 // wi...
732 0
全局变量和局部变量的理解
# 定义全局变量 a = 100 print(a) # def test1(): # a = 200 # print(a) # # # def test2(): # print(a) # 虽然没有定义变量a但是依然可以获取其数据 # # 调用函数 # test1() ...
1011 0