汇编角度看C中的堆与栈

转自: http://blog.csdn.net/leichelle/article/details/6932508


首先、堆与栈的区别

        一、预备知识—程序的内存分配

  一个由C/C++编译的程序占用的内存分为以下几个部分

  1、栈区(stack)— 由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。

  2、堆区(heap)— 由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表

  3、全局区(静态区)(static)— 全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。

  4、文字常量区 — 常量字符串就是放在这里的,程序结束后由系统释放 。

  5、程序代码区— 存放函数体的二进制代码。

  二、例子程序

  这是一个前辈写的,非常详细

  //main.cpp

  int a = 0; 全局初始化区

  char *p1; 全局未初始化区

  main()

  {

  int b; 栈

  char s[] = "abc"; 栈

  char *p2; 栈

  char *p3 = "123456"; 123456\0在常量区,p3在栈上。

  static int c =0; 全局(静态)初始化区

  p1 = (char *)malloc(10); 堆区

  p2 = (char *)malloc(20); 堆区

  }

  分配得来的10和20字节的区域就在堆区。

  strcpy(p1, "123456"); 123456\0放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。

        

          

接下来看看一个例子:

该例子描述的是print不带传参数的,要求在print中打印在调用函数中定义的一个数组序列的值。(要完成这要求只能先了解栈的知识,然后了解函数是如何被调用的,以及调用过程中都做些什么,以下具体描述),在VC6.0下编译:

#include <stdio.h>
 
void print()
{
    //这里进行打印arr数组,print不准传参数
 int *p,i;
   unsigned int _ebp;

    //该处为嵌入汇编
    __asm{
        mov _ebp,ebp
    }
    p=(int *)(*(int *)_ebp-4-4-4-4-8-7*4);
    for( i=0;i<7;i++)
        printf( "%d \n", p[i] );


}
int main()

    int s;
    int ss=0;
    char *str="fdsafdsafdsafdsafdsafdsafdsa";
    char fdsa='f';
    char srt[8];
    int arr[]={32,43,3,567,987,21,56};//数值随即

    print();
    return 0;
}

 

在VC6.0中的编译的汇编情况:

 

-- D:\VC_test\six\main.c 

//main函数部分

注:ebp与esp的区别:esp是堆栈指针寄存器,程序运行时时浮动的,不利于暂存其需要以后用到的数据。所以这工作只号交给ebp来做了,这个是基址寄存器,长用来存放某些函数发生调用时候的esp寄存器中的值。

 

17:   int main()

18:   {

00401050   push        ebp //保存调用main()函数的那个程序(一般为操作系统)的ebp中的值

00401051   mov         ebp,esp //保存esp到ebp

以上这两句主要功能是为了保存程序调用main()时候的自身的状态信息,一边执行完main()之后可以返回程序并且恢复原来的状态

00401053   sub         esp,74h //在main()函数中以调用函数的那个esp的值为基础分配一段74H空间大小的栈空间用于存放变量,以及调用函数相关的寄存器值(接下来的三个寄存器就是保存调用函数中的的信息ebx esi edi)

00401056   push        ebx

00401057   push        esi

00401058   push        edi

 

00401059   lea         edi,[ebp-74h]

0040105C   mov         ecx,1Dh

00401061   mov         eax,0CCCCCCCCh

00401066   rep stos    dword ptr [edi]

上面这四句话是把已经分配的74H的栈空间每个单元都初始化为0CCH

19:       int s=0;

00401068   mov         dword ptr [ebp-4],0

变量s 存放于栈空间地址ebp-4,以下同理

20:       int ss=0;

0040106F   mov         dword ptr [ebp-8],0

21:       char *str="fdsafdsafdsafdsafdsafdsafdsa";

00401076   mov         dword ptr [ebp-0Ch],offset string "fdsafdsafdsafdsafdsafdsafdsa" (00422f8c)

str存放的是字符串常量的地址,而栈内也是放字符串常量的地址,其内容放于堆中(处于数据段中)

22:       char fdsa='f';

0040107D   mov         byte ptr [ebp-10h],66h

23:       char srt[8];

24:       int arr[]={32,43,3,567,987,21,56};//数值随即

00401081   mov         dword ptr [ebp-34h],20h

00401088   mov         dword ptr [ebp-30h],2Bh

0040108F   mov         dword ptr [ebp-2Ch],3

00401096   mov         dword ptr [ebp-28h],237h

0040109D   mov         dword ptr [ebp-24h],3DBh

004010A4   mov         dword ptr [ebp-20h],15h

004010AB   mov         dword ptr [ebp-1Ch],38h

25://从上面汇编语句可以知道,对于数据,是在栈内先分出数组长度的空间,然后第一元素放于低地址,第二个元素放于次低地址,一次顺序。。。

26:       print();

004010B2   call        @ILT+0(_print) (00401005)

27:       return 0;

004010B7   xor         eax,eax

28:   }

004010B9   pop         edi

004010BA   pop         esi

004010BB   pop         ebx

004010BC   add         esp,74h

004010BF   cmp         ebp,esp

004010C1   call        __chkesp (004010f0)

004010C6   mov         esp,ebp

004010C8   pop         ebp

004010C9   ret

 //上面这几句是main()执行完了,返回调用程序时做的事情,恢复ebp esp 等

 //print()子函数部分

--- D:\VC_test\six\main.c 

1:    #include <stdio.h>

2:

3:    void print()

4:    {

0040D4E0   push        ebp

这语句很重要,把 ebp 的值( main()中分配的栈基地址 )压入栈中,此时esp中的值( 即存放ebp中的值的地址 )

 

0040D4E1   mov         ebp,esp

//把主函数中ebp寄存器的值的地址送到print中的ebp寄存器

0040D4E3   sub         esp,4Ch

0040D4E6   push        ebx

0040D4E7   push        esi

0040D4E8   push        edi

0040D4E9   lea         edi,[ebp-4Ch]

0040D4EC   mov         ecx,13h

0040D4F1   mov         eax,0CCCCCCCCh

0040D4F6   rep stos    dword ptr [edi]

5:        //这里进行打印arr数组,print不准传参数

6:        int *p,i;

//print()中的上面这几行作用于main()类似,每个被调用函数都会有这样的操作

7:          unsigned int _ebp;

8:        __asm{

9:            mov _ebp,ebp

//取print中的ebp寄存器的值到整型变量_ebp中

 这样的话_ebp就有这样的意思了:整型变量_ebp中的值为主函数main()中栈基地址的地址

0040D4F8   mov         dword ptr [ebp-0Ch],ebp

10:       }

 

11:       p=(int *)(*(int *)_ebp-4-4-4-4-8-7*4);

/*

这句话可能有点难理解,我是这么理解的,变量名_ebp其实在内存中就是一个地址(地址也是一个整型数值),在这个地址中存放的是unsigned int 类型的数值。如今因为_ebp中的那个unsigned int类型的值代表的意思是另外一个数的地址值,所以呢为了取出这个地址值,我们应该怎么做呢?我们可以这样做:

把_ebp的类型转换为int *_ebp的整型指针类型,如此一来这个_ebp中的值的意思就变成了一个特定值的地址,而我们为了得到这个特定值,可以通过指针操作符*来取出这个特定值,如*(int *_ebp) 。

*/

0040D4FB   mov         eax,dword ptr [ebp-0Ch]

0040D4FE   mov         ecx,dword ptr [eax]

0040D500   sub         ecx,34h

0040D503   mov         dword ptr [ebp-4],ecx

 

 

12:       for( i=0;i<7;i++)

0040D506   mov         dword ptr [ebp-8],0

0040D50D   jmp         print+38h (0040d518)

0040D50F   mov         edx,dword ptr [ebp-8]

0040D512   add         edx,1

0040D515   mov         dword ptr [ebp-8],edx

0040D518   cmp         dword ptr [ebp-8],7

0040D51C   jge         print+57h (0040d537)

13:           printf( "%d \n", p[i] );

0040D51E   mov         eax,dword ptr [ebp-8]

0040D521   mov         ecx,dword ptr [ebp-4]

0040D524   mov         edx,dword ptr [ecx+eax*4]

0040D527   push        edx

0040D528   push        offset string "%d \n" (0042201c)

0040D52D   call        printf (0040d770)

0040D532   add         esp,8

0040D535   jmp         print+2Fh (0040d50f)

14:

15:

16:   }

0040D537   pop         edi

0040D538   pop         esi

0040D539   pop         ebx

0040D53A   add         esp,4Ch

0040D53D   cmp         ebp,esp

0040D53F   call        __chkesp (004010f0)

0040D544   mov         esp,ebp

0040D546   pop         ebp

0040D547   ret

 

 

注:这是本人的一些理解,不对的地方还望各位同道人士指点指点。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值