vc++内存机理

【转】原文地址:http://blog.csdn.net/rryqsh/article/details/3867572

内存的概念就不赘述了,在此主要叙述一下VC++中指针,地址,堆,栈的个人理解,如果有错请不吝指出。

    地址是什么?

    地址是一个变量在内存中的“门牌号码”,要得知一个变量的值,只要根据变量的地址就能得到这个地址中到底住的是什么人(这个变量的值到底是什么)。

    对于值类型的变量来说,一个地址就能对应一个变量了。但是对于大小超过一个内存块的引用类型,当这个引用类型在一个地址对应的内存块中放不下,这个引用类型的地址就是它的多个内存块的首地址,程序可以通过这个首地址得到一整块内存,也就得到了这个对象。

    其中涉及了对象的内存分配原则,关于这个请参见其它内容。在本文中,主要记住,地址就是变量的首地址。

    比如我们如下代码:

 


 
  1. int a=0;  
  2. int b=0;  
  3. cout<<"Address Of a Is:"<<&a<<endl;  
  4. cout<<"Address Of b Is:"<<&b<<endl;  

 

    它们的值都是0,但是它们的地址&a和&b是不一样的,输出结果如下:

Address Of a Is:0012FE58
Address Of b Is:0012FE4C

 

    指针是什么?

    指针也是一种变量,我们可以理解为指针就是专门记录地址的值类型。因为我们可以通过首地址得到变量,所以一个变量不管多大,需要多少内存,只需要一个指针就能指向它了。

    因为指针也是变量,所以指针是有自己的地址的(没有地址,指针记录的目标的地址存在哪儿呢?对吧),比如我们接着看如下的代码

 


  1. int *p1=&a;  
  2. int *p2=0;  
  3. int *p3=0;  
  4. cout<<"Address Of p1 Is:"<<&p1<<endl;  
  5. cout<<"Address Stores In p1 Is:"<<p1<<endl;  
  6. cout<<"Address Of p2 Is:"<<&p2<<endl;  
  7. cout<<"Address Of p3 Is:"<<&p3<<endl;  

 

    我们为指针p1赋值是变量a的地址,所以p1的值就是&a。

    我们为p2赋的值是在栈内存中开辟的一个新的数字0的地址。为什么不用&0?这是编译器的要求,虽然理解上容易了很多,但是每次这样写岂不是很累人?在大家都知道的情况下,我们就不要用这么麻烦的写法了。

    为什么还要给p3也赋上0的地址?这是为了证明这个0不是只有一个,虽然看起来都是0,但是p2指向的0不是p3指向的0。看结果,大家就明白了。

Address Of p1 Is:0012FE40
Address Stores In p1 Is:0012FE58
Address Of p2 Is:0012FE34
Address Of p3 Is:0012FE28

    这个结果说明指针是有自己的地址的,同时,指针记录的值就是它所指向的变量的地址,比较一下一开始的代码中变量a的地址“&a”和现在p1的值,都是:0012FE58,说明我们只要通过p1就能找到a,就能找到a变量内存块中的值了。

 

    再深究一下指针如何?

    因为指针也是一个变量,所以,我们当然可以做一些很匪夷所思的事情,比如:用一个指针指向一个指针!请看下面的代码。

 


 
  1. int **pp=&p1;  
  2. cout<<"Address Of pp Is:"<<&pp<<endl;  
  3. cout<<"Address Of p1 Is:"<<pp<<endl;  
  4. cout<<"Address Stores In p1 Is:"<<*pp<<endl;  

 

    小凡一开始学C++看到这个代码的时候差点就要骂街了,这是什么东西啊。其实就是一个指针的指针,pp就是Pointer to Pointer的缩写,呵呵。在pp中记录的是p1的地址,同时pp也有自己的内存地址。看看结果是不是像我们想的一样?

Address Of pp Is:0012FE1C
Address Of p1 Is:0012FE40
Address Stores In p1 Is:0012FE58

    pp的内存地址是全新的,这是废话,因为pp是一个新的变量,当然需要一个新的内存块来存储它的值。同时pp的值就是p1的地址,上面我们已经用&p1来输出验证了。同时,我们用*pp来获得了p1中的值。

    *符号就是解引用符,说白了,就是找到[内存地址=变量中的值]的那个内存块中的值。所以我们的*pp就是:根据pp中存储的p1的地址找到了p1,并且输出了p1内存块中的值,也就是变量a的地址&a。

 

    有点绕人不是吗?其实只要记住简单的一点:地址是自动分配的,不能人为改变;指针是记录地址用的,我们可以用过解引用符*通过指针记录的地址来获取这个首地址记录的变量。就这么简单。

 

堆和栈

说完了地址和指针,我们来说说堆和栈的不同,在此之前,感谢Polaris给我的帮助,在这个问题是他给我讲了很多,也举了很多例子,让我懂了很多关于堆栈的东西。

    首先,关于堆和栈的数据结构小凡就不多说什么了,是先进先出还是后进先出也不是我们讨论的范围。

    但是有一点还是很不一样的,栈内存空间一般而言要比堆内存空间小很多,对windows来说,一个thread的栈内存是1M,但是堆内存可以按照你的需要申请甚至好几个G(当然到时候要用硬盘缓存这个就是题外话了)。

    从编码上看,也是有一些区别的。

 

  1. int intInStack=0;  
  2. int *intInHeap=(int *)malloc(sizeof(int));  
  3. *intInHeap=0;  
  4. delete intInHeap;  

 

    在以上的代码中,intInStack就是存放在栈内存中的0,而intInHeap就是一个指向堆内存中的0的指针。没错,这就是C++分配堆内存和栈内存在代码上的区别,对C++来说,不管是值类型还是引用类型,都是通过这样的方式来申请堆内存和栈内存。而且,有一点更重要:堆内存是需要自维护的!delete intInHeap;如果不写,那这块内存就永远不释放,甚至程序关闭了也不会释放,这就是有名的内存溢出问题。

   

    题外话,C#中的堆栈内存分配方式:

    C#中,除了class以外,其它类型其实都是值类型,包括struct。小凡想很多人都认为new的就是分配在堆内存上了,事实上:

  int i = 0;
  int i2 = new int();

  int i3 = new Int32();

    这三行代码是等价的,它们都是值类型,所以,并不是C#中的new就保证在堆内存上。

    其实很简单,C#中是通过class声明的变量,就是引用类型,引用类型一律放在堆内存中。其它类型都是值类型,一律放在栈内存中

    什么?你问Int32?仔细看看,它是struct,你试试继承Int32看看就知道了。为什么要new?因为如果不new一下,所有变量都出于未分配状态,就好像是一个没有分配目标内存地址的指针,肯定是不能使用的。

posted @ 2013-12-14 23:35  levar  阅读(243)  评论(0)    收藏  举报