前提知识

  c0s调用main函数的地址:  11ah

  main函数的连接地址:  01fah

一、全局变量与局部变量

测试程序

int a1,a2,a3;

void f(void);
void g(void);
void h(void);
main()
{
int b1,b2,b3;
a1 = 0xa1;a2 = 0xa2;a3 = 0xa3;
b1 = 0xb1;b2 = 0xb2;b3 = 0xb3;
} void f(void)
{
int c1,c2,c3;
a1 = 0x0fa1;a2 = 0x0fa2; a3 = 0x0fa3;
c1 = 0xc1; c2 = 0xc2; c3 = 0xc3;
c1 = c2 + c3;
} void g(void)
{
int i = ;
while(i--);
} void h(void)
{
int h1,h2,h3,h4,h5,h6,h7;
h1 = 0xc1; h2 = 0xc2; h3 = 0xc3;h4 = 0xc4;
h5 = 0xc5; h6 = 0xc6; h7 = 0xc7;
h1 = h2 + h3;
h2 = h3 + h4;
}

编译、连接后,用debug调试这段代码,根据函数分别贴出对应的反汇编代码

1、main函数

(1)全局变量

main()
{
int b1,b2,b3;
a1 = 0xa1;a2 = 0xa2;a3 = 0xa3;
b1 = 0xb1;b2 = 0xb2;b3 = 0xb3;
}

对应的反汇编代码

  可以看到全局变量,a1、a2、a3的地址分别是ds:[01a6]、ds:[01a8]、ds:[01aa]。

  可以看到,ds:[01a6]的物理地址是16266h,而程序的结束位置是CS:[2a0]的物理地址是15d60。可见,全局变量位于代码段外。ds=ss,而sp=ffe6,ss:sp的物理位置为260a6h,即栈顶位于260a6h,栈应高于栈顶。所以全局变量不可能位于栈区。

综上所述,我认为全局变量位于非代码段,非栈段,而位于data段(初始化)或者bss段(未初始化)。

(2)局部变量

 开辟在栈中的局部变量 

  a) 编译器先将BP压入栈

  b)用BP保存栈指针,然后SP-6,为局部变量开辟空间。

    push bp

    mov bp,sp

    sub sp,+6

  c) 函数返回前恢复栈,释放局部变量空间

    mov sp,bp

  d) 恢复BP

2、f函数

void f(void)
{
int c1,c2,c3;
a1 = 0x0fa1;a2 = 0x0fa2; a3 = 0x0fa3;
c1 = 0xc1; c2 = 0xc2; c3 = 0xc3;
c1 = c2 + c3;
}

  对应的反汇编代码

  局部变量c1、c2被开辟在寄存器SI、DI中,而c3则开辟在栈中。

  开辟在寄存器中的局部变量 

  a) 编译器先将BP压入栈

  b)用BP保存栈指针,将SI、DI压入栈,从而为局部变量开辟空间

    push bp

    mov bp,sp

    push si

    push di

  c) 函数返回前恢复寄存器(释放局部变量),然后恢复栈

    pop di

    pop si

    mov sp,bp

  d) 恢复BP

  无论开辟到栈中,还是开辟在寄存器中,栈指针的移动都是相同的。

3、g函数

void g(void)
{
int i = ;
while(i--);
}

  对应的反汇编代码

  通过这个函数的反汇编可以重复验证“开辟在寄存器中的局部变量”的结论。

4、h函数

 还会开辟到其他地方吗?

void h(void)
{
int h1,h2,h3,h4,h5,h6,h7;
h1 = 0xc1; h2 = 0xc2; h3 = 0xc3;h4 = 0xc4;
h5 = 0xc5; h6 = 0xc6; h7 = 0xc7;
h1 = h2 + h3;
h2 = h3 + h4;
}

 对应的反汇编代码

  可以看到,当有7个局部变量的情况下,局部变量除了开辟在栈中,就是寄存器中,没别的地方了。

 什么时候开辟在栈中,什么时候开辟在寄存器中?这个问题还搞不懂,不过感觉没多大意义。

二、函数如何传递参数,又是如何接受的呢

  测试程序

void showchar(char a,char b);

main()
{
showchar('a',);
} void showchar(char a,char b)
{
char r;
r = a;
r = b;
}

1、main函数

main()
{
showchar('a',);
}

对应的反汇编代码

a) 函数的参数是通过栈传递,而且是从右到左依次入栈

b) 即使是char型变量,在传递参数时,也是占用两个字节,因为push操作是两个字节为单位的。取的时候,是按照它的类型来的

c) 释放参数可以通过多次pop来实现。事实上,有时是通过“add sp,+数值”来实现的。显然,后者释放空间来的更快。

2、showchar函数

void showchar(char a,char b)
{
char r;
r = a;
r = b;
}

对应的反汇编代码

d) 调用函数前后堆栈保持一致,也就是函数返回时要让堆栈指针恢复到和进入函数时一样的状态

e) 函数接受形参是通过从栈中取的

f) 对于为初始化的局部变量,编译器是不会给它赋初值的,而是拿来就用

g) main函数的局部变量的寿命要比调用函数showchar的形参寿命长

h) BP不仅保存了堆栈的值,而且通过它可以找到传入参数的值,BP+4是第一个参数,BP+6是第二个参数......取参数是从左到右取的(这就是堆栈的妙处)

三、函数返回值

测试程序

char f(void);

main()
{
char c;
c = f();
} char f(void)
{
return 'a';
}

1、main函数

char f(void);

main()
{
char c;
c = f();
}

对应的反汇编代码

2、f函数

char f(void)
{
return 'a';
}

对应的反汇编代码

函数返回值:

  char型 AL

  int型 AX  

四、总结

1、全局变量

  全局变量位于非代码段,非栈段,而位于data段(初始化)或者bss段(未初始化)。

2、局部变量

a、开辟在堆栈中的局部变量,通过“mov sp,bp”来释放空间

b、开辟在寄存器中的局部变量(push si/di),通过"pop si/di”来实现

c、未初始化的局部变量,编译器并不会给它赋初值,而是拿来就用

3、如何传递参数(主函数)

a、函数的参数是通过栈传递,而且是从右到左依次入栈

b、即使是char型变量,在传递参数时,也是占用两个字节,因为push操作是两个字节为单位的。

c、showchar('a',2)这样的传入两个常数,也会在堆栈中开辟两个空间,也即对应两个实参变量。

4、函数如何接收参数(子函数)

a、 函数接受形参是通过从栈中取的

b、通过BP可以找到传入参数的值,BP+4是第一个参数,BP+6是第二个参数......取参数是从左到右取的

5、如何释放参数(主函数)

  释放参数可以通过多次pop来实现。事实上,有时是通过“add sp,+数值”来实现的。

6、函数返回值

 char型 AL

 int型 AX  

五、其他结论

1、局部变量、传递参数和接收参数都与堆栈脱不了干系

  一个结论:局部变量、传递参数和接收参数都伴随着入栈、出栈、读堆栈中的内容等操作。对堆栈的这些操作,完成了变量和参数的创建、使用和释放。

  开辟在堆栈中的变量和参数都是在堆栈中开辟空间的,释放的时候也就通过移动sp或者pop指令来完成。

  开辟在寄存器中的变量,是通过“push si/di”来获取空间的,释放的时候是通过“pop si/di”来完成的。

2、C语言函数汇编后的代码

push bp
mov bp,sp
...
...
mov sp,bp
pop bp
ret

  这些代码是每个函数反汇编都会出现的,怎样理解呢?

a、BP保存了堆栈的值,所以可以利用堆栈来开辟局部变量的空间。(倘若不保存堆栈的值,将来怎样回收这些空间呢?可能会比较麻烦)

b、通过BP可以找到传入参数的值,BP+4是第一个参数,BP+6是第二个参数......取参数是从左到右取的(这就是堆栈的妙处)

参考:王爽汇编语言综合研究-函数如何接收不定数量的参数

   王爽汇编语言综合研究-使用内存空间

   《汇编语言》319页研究实验3 “使用内存空间”

用汇编语言研究C语言的全局变量、局部变量、参数、返回值放在哪里的更多相关文章

  1. day03 函数基本语法及特性 2. 参数与局部变量 3. 返回值 嵌套函数 4.递归 5.匿名函数 6.函数式编程介绍 7.高阶函数 8.内置函数

    本节内容 1. 函数基本语法及特性 2. 参数与局部变量 3. 返回值 嵌套函数 4.递归 5.匿名函数 6.函数式编程介绍 7.高阶函数 8.内置函数 温故知新 1. 集合 主要作用: 去重 关系测 ...

  2. Swift2.0语言教程之函数的返回值与函数类型

    Swift2.0语言教程之函数的返回值与函数类型 Swift2.0中函数的返回值 根据是否具有返回值,函数可以分为无返回值函数和有返回值函数.以下将会对这两种函数类型进行讲解. Swift2.0中具有 ...

  3. c语言 局部变量做返回值 问题

    一般的来说,函数是可以返回局部变量的. 局部变量的作用域只在函数内部,在函数返回后,局部变量的内存已经释放了.因此,如果函数返回的是局部变量的值,不涉及地址,程序不会出错.但是如果返回的是局部变量的地 ...

  4. 关于C语言函数调用压栈和返回值问题的疑惑

    按照C编译器的约定调用函数时压栈的顺序是从右向左,并且返回值是保存在eax寄存器当中.这个命题本该是成立的,下面用一个小程序来反汇编观察执行过程: #include<stdio.h> in ...

  5. C语言 realloc为什么要有返回值,realloc返回值具体解释/(解决随意长度字符串输入问题)。

    在C语言操作中会用到大量的内存操作,当中非经常常使用的一个是realloc(). 由字面意思能够知道,该函数的作用是用于又一次分配内存. 使用方式例如以下: NewPtr=(数据类型*)realloc ...

  6. 013_go语言中的函数多返回值

    代码演示 package main import "fmt" func vals() (int, int) { return 3, 7 } func main() { a, b : ...

  7. r语言 function 指定多个返回值

    # Goals: To write functions # To write functions that send back multiple objects. # FIRST LEARN ABOU ...

  8. C语言中赋值表达式的返回值是什么?

    我们或多或少都有过,或者见过将赋值表达式参与运算的情况.这通常会伴随着一些意想不到的问题.今天我就见到了一段奇怪的代码: #include<stdio.h> int main() { ; ...

  9. 深入研究C语言 第二篇(续)

    1. 关于如下的程序,关于结构体的拷贝,拷贝是拷贝到内存中的什么地方? 我们进入debug进行反汇编,单步等操作跟踪查看.发现: 在main中,我们看到call 0266应该对应的是转跳到func处执 ...

随机推荐

  1. linux 监控命令

    先总结下常用的一些监控工具: ##linux命令 w 系统负载 lsof -p pid 进程打开的文件 lsof -i:port 端口的运行情况 free -m 内存情况 vmstat 进程.内存.内 ...

  2. druid报异常 “sql injection violation, part alway true condition not allow”的解决方案

    使用durid连接池组件,执行sql时发现异常如下: Caused by: java.sql.SQLException: sql injection violation, part alway tru ...

  3. mysql创建数据库(指定编码)

    如下脚本创建数据库yourdbname,并制定默认的字符集是utf8. CREATE DATABASE IF NOT EXISTS yourdbname DEFAULT CHARSET utf8 CO ...

  4. UVaLive6039 Uva1668 Let's Go Green

    一开始考虑所有边都是单独的一条路径 然后尽量多的合并 #include<cstdio> #include<cstring> #include<cstdlib> #i ...

  5. JAVA去掉字符串前面的0

    最佳方案:使用正则 String str = "000000001234034120"; String newStr = str.replaceAll("^(0+)&qu ...

  6. 【Linux常用工具】1.1 diff命令的三种格式

    diff是用来比较两个文本文件的差异的工具,它有三种格式,下面用实例介绍一下: 准备三个测试文件1.txt 2.txt 3.txt bixiaopeng@bixiaopengtekiMacBook-P ...

  7. xp系统

    产品密钥 MRX3F-47B9T-2487J-KWKMF-RPWBY(亲测可用)

  8. [转] 考验你的JavaScript底细

    http://sentsin.com/ 尽管今日的JavaScript已经突飞猛进,但JS的许多特性仍然保留,以下题目并不是有意设坑,许多地方将验证你的JS底细,如果错了一半,请别告诉我你从事前端. ...

  9. 学习微信小程序之css15解决父盒子高度塌陷

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  10. 偶遇问题 - - JavaScript 取消链接默认行为问题

    今天在测试<JavaScript DOM编程艺术(第2版)>中第69页代码时,遇到了问题.本来预期效果应该是点击链接后不跳转当前页面,而是另外弹出有个窗口.但结果却是页面跳转了.代码如下图 ...