深入理解函数中分配内存的问题

简介:

先看这样的代码

 1 void  MyNew( int   * p)
 2 {
 3    p = new int;
 4}

 5
 6 int  main()
 7 {
 8    int *= NULL;
 9    MyNew(p);
10}


开始写了一篇函数中分配内存的问题,通过说明他们产生了拷贝,而导致p不能成功分配。但并未提出事实根据,下面我们来仔细看看具体原因。

我们需要弄清两点:1、main函数中的p与MyNew函数中的p是不是一样;2、如果不一样,是怎么导致了不一样的。

第一点很好看,我们可以在编译器(VC环境,我用的是VS 2005)的监视窗口中跟踪p的地址。 
         在监视窗口中增加一个对 &p的监视,然后我们在int*p = NULL处添加一个断点。单步执行,停在MyNew函数前,此时我们可以看到,&p的值为 0x0012ff60 .   然后,我们单步进入MyNew函数,此时我们可以发现,&p的值变成了0x0012fe8c   明显,它们不是同一个东西,这样在MyNew操作的时候,操作的就不是我们想要操作的那个p(0x0012ff60). 好了,不要去猜测这两个数字之间的关系,接下来会给你一个满意的答案.

先看看下面这个反汇编的结果

 int *p = NULL;
0041153E  mov         dword ptr [p],0 

 MyNew(p);
00411545  mov         eax,dword ptr [p] 
00411548  push        eax  

00411549  call        MyNew (41116Dh) 

红色部分就是将p作为参数压栈,然后call MyNew,注意,此时我们的p已经被保存起来了。


void MyNew(int *p)
{
004114C0  push        ebp  
004114C1  mov         ebp,esp 
004114C3  sub         esp,0CCh   //红色:分配33*4Bytes 临时空间 
004114C9  push        ebx  
004114CA  push        esi  
004114CB  push        edi  
004114CC  lea         edi,[ebp-0CCh] 
004114D2  mov         ecx,33h 
004114D7  mov         eax,0CCCCCCCCh 
004114DC  rep stos    dword ptr es:[edi]    //蓝色:初始化分配的空间为 0xcccccccc

 p = new int;
004114DE  push        4    
004114E0  call        operator new (411190h)   //调用new 返回值存放于eax中。
004114E5  add         esp,4 

004114E8  mov         dword ptr [ebp-0C8h],eax  //将new出来的地址放到ebp-0c8h中
004114EE  mov         eax,dword ptr [ebp-0C8h]  //将new出来的值放到eax中,作为返回值。
004114F4  mov         dword ptr [p],eax //将eax中的值放入p中  这也是为什么 int* MyNew() {return new int;}能成功的原因
}

//下面是清栈操作
004114F7  pop         edi  
004114F8  pop         esi  
004114F9  pop         ebx  
004114FA  add         esp,0CCh  //清除临时变量
00411500  cmp         ebp,esp 
00411502  call        @ILT+325(__RTC_CheckEsp) (41114Ah) 
00411507  mov         esp,ebp 
00411509  pop         ebp  
0041150A  ret     


上面的东西不能说明根本问题,因为没有作任何分析,下面我们就来仔细分析一下,特别是最后的004114F4  mov         dword ptr [p],eax   有人就会问,既然已经放回了p中,为啥p还是没变呢。


这就是new之间的堆栈空间示意图,可以看出,我们传入的参数是ebp+8,而当new回来后,却用的是004114E8  mov         dword ptr [ebp-0C8h],eax  很明显,ebp-0c8 是临时分配的空间。 而放入的那个p, 的确,它是放了,但是,这个p,已经不是那个p了,这个p指向的是我们栈空间里的临时变量,当函数返回后,p自然就清除了。 所以,最后,main函数中,p所指向的地址并没有改变。








void  MyNew( int   *& p)
{
    p 
= new int;
}


我们将代码稍作修改,改成传递指针的引用,那又会发生什么呢。首先,我们按照上面的方法检测其地址。 你会发现,两个函数中的地址都是 0x0012ff60.
那,为什么会这样呢,我们看看两个地方,第一就是参数传递时的压栈。
00411535  lea         eax,[p] 
00411538  push        eax
  
00411539  call        MyNew (4111F4h) 

可以看出,这次传递的,并非是像开始一样 mov eax, dword ptr[p]   。二者的差别在于,上一次(没有采用引用传递)传递的是值,而这一次(采用了引用传递)传递的是指针p的地址。

接下来,我们再来看看刚刚new出来之后赋值的地方。

004114E8  mov         dword ptr [ebp-0C8h],eax 
004114EE  mov         eax,dword ptr [p] 
004114F1  mov         ecx,dword ptr [ebp-0C8h] 
004114F7  mov         dword ptr [eax],ecx
 


可以发现,这正是我们传中说的:取得p的地址,采用*p求出p所指向的地址。然后对*p赋值,以改变它的值。。

还有一种就是指针的指针void MyNew(int** p){*p = new int}的方式,其实这个传递引用是完全等效的。甚至,反汇编后,他们是同样的代码。

结论:从上面的代码中我们可以看到,如果想要在函数内改变参数的值,则只能通过传递他的地址(引用也是传地址)方式,然后对其地址指向的内容进行变更!!!



终于写完了。有很多地方觉得还是没讲清楚,希望各位大大指教,小弟立马修改。 洗过头,上班去!!

作者:码瘾少年·麒麟子 
出处:http://www.cnblogs.com/geniusalex/ 
蛮牛专栏:麒麟子 
简介:09年入行,喜欢游戏和编程,对3D游戏和引擎尤其感兴趣。 
版权声明:本文版权归作者和博客园共有,欢迎转载。转载必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

转载:http://www.cnblogs.com/geniusalex/archive/2010/05/05/1940496.html

目录
相关文章
|
14天前
|
程序员 C语言
C语言库函数 — 内存函数(含模拟实现内存函数)
C语言库函数 — 内存函数(含模拟实现内存函数)
24 0
|
25天前
|
编译器 C语言 C++
【C语言】memset()函数(内存块初始化函数)
【C语言】memset()函数(内存块初始化函数)
26 0
|
25天前
|
编译器 C语言 C++
【C语言】memcpy()函数(内存块拷贝函数)
【C语言】memcpy()函数(内存块拷贝函数)
42 0
|
1月前
|
C语言
模拟实现C语言中经典库函数,字符相关的函数与内存相关的函数
模拟实现C语言中经典库函数,字符相关的函数与内存相关的函数
模拟实现C语言中经典库函数,字符相关的函数与内存相关的函数
|
1月前
|
算法 C语言
C语言中的内存函数你知道多少呢?
C语言中的内存函数你知道多少呢?
|
1月前
|
编译器 C语言
【C语言】动态内存函数介绍
【C语言】动态内存函数介绍
|
2天前
|
编译器 C语言
字符串与内存函数
字符串与内存函数
14 0
|
25天前
|
编译器 C语言 C++
【C语言】calloc()函数详解(动态内存开辟函数)
【C语言】calloc()函数详解(动态内存开辟函数)
25 0
|
25天前
|
存储 编译器 程序员
【C语言】内存的动态分配与释放
【C语言】内存的动态分配与释放
27 0

热门文章

最新文章