前提知识

  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. Apache-POI操作Excel的一些小技巧

    Apache-POI操作Excel将合并后的单元格全部填充为相同数据的一个实例. private static void fillMergedRegion(final Sheet sheet) { f ...

  2. 324. Wiggle Sort II

    这个题真是做得我想打人了.我生起气来连自己都打. 一开始quick select没啥可说的.但是in place把老命拼了都没想出来.. 看网上的答案是3 partition,MAP式子一看就由衷地反 ...

  3. 放爬虫nginx

    禁止火狐浏览器访问 例子

  4. 【设计模式 - 12】之代理模式(Proxy)

    1      模式简介 1.1    定义 为其他对象提供一种代理以控制对这个对象的访问.代理对象起到中介作用,可以去掉功能服务或增加额外服务. 1.2    常见的代理模式 1)        远程 ...

  5. maven tomcat1.7环境下构建javaweb 项目

    tomcat用户权限设置 在tomcat安装路径\conf目录下tomcat-users.xml添加: <role rolename="admin-gui"/> < ...

  6. 新秀学习Hibernate——简单的增加、删、更改、检查操作

    部分博客使用Hibernate单的样例,把数据库的映射显示了出来在上一篇的博客基础上这篇博客讲述怎样利用Hinbernate框架实现简单的数据库操作. 1.增加junit.jar 2.新建一个工具类H ...

  7. [转] 使用git自动部署简单网站

    要做什么 假设你有一个博客,有一台网站服务器(或者很多台作负载均衡的服务器),当你的博客要升级时,你可能要在你自己的电脑上写好代码(可能包括本地调试好),然后提交到git(或svn),然后在每个服务器 ...

  8. android学习笔记----JNI中的c控制java

    面向对象的底层实现 java作为面向对象高级语言,可对现实世界进行建模.和面向过程不同的是面向对象软件的编写不是流程的堆积,而是对业务逻辑的多视角分解和分类.其过程大致为:      1).将知识分解 ...

  9. 用户输出表单处理php

    php中的表单输入处理,我用两个文件,在linux输出: touch php_post1.html php_post1.php php_post1.html代码如下: <!doctype htm ...

  10. PHP错误类型及屏蔽方法

    1. 注意(Notices)这些都是比较小而且不严重的错误,比如去访问一个未被定义的变量.通常,这类的错误是不提示给用户的,但有时这些错误会影响到运行的结果. 2. 警告(Warnings)这就是稍微 ...