前提知识

  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. ADO.NET对象模型

    ADO.NET建立在NetFramwork一些核心类的基础之上,可以将这些类分为两组:用于包含于管理数据的容器类与用于连接特定数据源的控制类. 容器类是通用的,无论使用什么样的数据源,都可以使用相同的 ...

  2. shared pool详解

    共享池shared pool的概念用户提交的命令:解析.执行用户命令的解析解析的过程是一个相当复杂的过程,它要考虑各种可能的异常情况比如SQL语句涉及到的对象不存在.提交的用户没有权限等等而且还需要考 ...

  3. in_array严格模式和普通模式的区别

    貌似是因为test转整型变0  0和0 匹配能成功 返回真 启用严格模式发现没有这个问题

  4. 【Android - MD】之NavigationView的使用

    NavigationView是Android 5.0新特性--Material Design中的一个布局控件,可以结合DrawerLayout使用,让侧滑菜单变得更加美观(可以添加头部布局). Nav ...

  5. jQuery Mobile 图标无法显示

    对jquery mobile来说,使用data-icon属性配置,可以设置元素的图标.图标没有变成右箭头,而是如下图所示: //已经设置了图标 ,data-icon="home" ...

  6. 移动开发框架,第【一】弹:QuoJs 官方文档(汉化版)

    作者:一只猿 原文地址: http://www.92ez.com 转载请注明出处,谢谢 帮助说明 如果您认为QuoJS只是一个触摸事件管理器,那你就错了,它是一个功能丰富的类库,无需第三方JavaSc ...

  7. 构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(35)-文章发布系统②-构建项目

    原文:构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(35)-文章发布系统②-构建项目 注:阅读本文,需要阅读本系列的之前文章 代码生成器下载地址(文章开头处) ...

  8. struts2学习笔记(2)---Action中訪问ServletAPI获取Map类型的Servlet元素

    源码: strust.xml <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts ...

  9. Linux入门基础 #5:Linux文件系统挂载管理

    本文出自   http://blog.csdn.net/shuangde800 ------------------------------------------------------------ ...

  10. bt 介绍以及 bt 种子的hash值(特征值)计算

    bt种子的hansh值计算,近期忽然对bt种子感兴趣了(原因勿问) 1. bt种子(概念) bt 是一个分布式文件分发协议,每一个文件下载者在下载的同一时候向其他下载者不断的上传已经下载的数据,这样保 ...