new、delete、指向連續空間的指針、數組、空間釋放、空間申請[C++][內存管理]


一. 使用new和delete運算符時PF率的變化情況
Ctrl+Alt+Del進入任務管理器、性能,運行下列代碼,並觀察PF率的變化。可知,new運算符增加PF率,delete使PF率還原。 注意:使用 new 得來的空間,必須用 delete 來釋放;使用 new [] 得來的空間,必須用 delete [] 來釋放。彼此之間不能混用。 new [] 分配出連續空間后,指針變量“指向”該空間的首地址。
#include < iostream.h >
#include
< stdio.h >

int  main( int  argc,  char   * argv[])
{
    cout
<<"按任意鍵開始分配內存"<<endl;
    getchar();

    unsigned 
char *= new unsigned char[1024*1024*100];
    cout
<<"成功分配了100M的內存"<<endl;
    getchar();

    delete []p; 
    cout
<<"釋放所分配的100M內存"<<endl;
    
return 0;
}



二. 指向連續空間的指針
在通過 new [] 指向連續空間以后,p 就變得和一個一維數組很是類似。我們先來復習一下數組相關知識。假設是這么一個數組: int arr[20]; 則arr 的內存示意圖為

      和指針變量相比,數組沒有一個單獨的內存空間而存放其內存地址。即:指針變量p是一個獨立的變量,只不過它的值指向另一段連續的內存空間;而數組arr,本身代表的就是一段連續空間。
      數組是“實”的地址,不能改變。當你和定義一個數組,則這個數組就得根據它在內存中的位置,得到一個地址,如上圖中的“0x1A000000”。只要這個數組存在,那么它終生的地址就是這個值。
      指針是一個“虛”的地址,可以改變地址的值。當你定義一個指針變量,這個變量占用4個字節的內存,你可以往這4字節的內存寫入任意一個值,該值被當成一個內存地址。比如,你可以寫入上面的“0x1A000000,此時,指針p指向第一個元素。也可以改為“0x1A000003”,此時,指針p指向第二個元素。
      所以,當p通過 new [] 指向一段連續空間的結果是,p 是一個指向數組的指針,而*p是它所指的數組。

兩者的相似之處:
數組:

int  arr[ 20 ];  // 定義
arr[ 0 =   100 // 讓第一個元素為100
for  ( int  i  =   1 ; i  <   20 ; i ++ )
{
    arr[i] 
= arr[i-1+ 50;
}

for  ( int  i  =   0 ; i  <   20 ; i ++ // 輸出
{
    cout 
<< arr[i] << endl;
}

// 通過+來得到指定元素,也可通過[]
cout  <<   * (arr  +   0 <<  endl;  // *(arr+0) 等於 *arr
cout  <<   * (arr  +   1 <<  endl;
cout 
<<   * (arr  +   1 <<  endl;

指針:
int   * =   new   int [ 20 ];  // 定義
p[ 0 =   100 // 讓第一個元素為100
for  ( int  i  =   1 ; i  <   20 ; i ++ )
{
    p[i] 
= p[i-1+ 50;
}

for  ( int  i  =   0 ; i  <   20 ; i ++ // 輸出
{
    cout 
<< p[i] << endl;
}

// 通過+來得到指定元素,也可通過[]
cout  <<   * (p  +   0 <<  endl;  // *(p+0) 等於 *p
cout  <<   * (p  +   1 <<  endl;
cout 
<<   * (p  +   1 <<  endl;

兩者的不同之處:
數組:
// 定義並初始化
int  arr[ 10 =   {0,1,2,3,4,5,6,7,8,9}

// 不能通過對數組本身+或-來改變數組的位置
arr  =  arr  +   1 // 錯!
cout  <<   * arr  <<  endl;
arr
++ // 錯!
cout  <<   * arr  <<  endl;
arr
-- // 錯!
cout  <<   * arr  <<  endl;

// 數組所帶的空間由系統自動分配及回收,無須也無法由程序來直接釋放

指針:
// 定義並且生成空間,但不能直接初始空間的內容
int   * =   new   int [ 20 {0,1,2,3,4 ……} //  錯!

// 只得通過循環一個個設置
for  ( int  i = 0 ; i < 20 ; i ++ )
{
    p[i] 
= i;
}


// 可以通過+或-操作直接改變指針
=  p  +   1 ;
cout 
<<   * <<  endl;

p
++ ;
cout 
<<   * <<  endl; 

p
-- ;
cout 
<<   * <<  endl;

// 指向連續空間的指針,必須使用delete[]來釋放
delete [] p;

三. delete/delete[]的幾個注意點
1.
指針通過 new 或 new[] ,向系統“申請”得到一段內存空間,這段 內存空間必須在不需要將它釋放了。
int *  p  =   new   int [ 100 ]; 
 
int  girl[ 100 ];  
  
=  girl;  
  
delete [] p;
   災難在 delete [] p 時發生。 我們原意是要釋放p最初通過new int[100] 而得到的內存空間,但事實上,p那時已經指向girl[100] 了。結果,第一、最初的空間並沒有被釋放。第二、girl[100] 本由系統自行釋放,現在我們卻要強行釋放它。

2. 一個指針被刪除時,應指向最初的地址
當一個指針通過 +,- 等操作而改變了指向;那么在釋放之前,應確保其回到原來的指向。如下所示:在 delete [] p 時,p指向的是第二個元素,結果該釋放將產生錯位:第一個元素沒有被釋放,而在最后多刪除了一個元素。
int   * =   new   int [ 3 ];

* =   1 ;
cout 
<<   * <<  endl;

p
++ // p的指向改變了,指向了下一元素

* =   2 ;
cout 
<<   * <<  endl;

delete [] p; 
// 錯誤的釋放
如何消除這一嚴重錯誤呢?
第一種方法是把指針正確地 ""回原始位置:
p -- ;

delete [] p;

但當我們的指針指向變化很多次時,在釋放前要保證一步不錯地一一退回,會比較困難。所以另一方法是在最初時“備份”一份。在釋放時,直接釋放該指針即可。
int *  p  =   new   int [ 3 ];

int *  pbak  =   * p;  // 備份

// 移動 p

……

delete [] pbak; 
// 釋放
由於 pbak正是指向p最初分配后的地址,我們刪除 pbak, 就是刪除p最初的指向。此時我們不能再刪除一次p。這也就引出new / delete new[] / delete[] 在本章的最后一個問題。

3. 已釋放的空間,不可重復釋放
第一種最直接:
int *  p  =   new   int ( 71 );

cout 
<<   * <<  endl; 

delete p; 
// OK!

delete p; 
// ERROR! 重復刪除p

第二種為重復刪除同一指向的多個指針
int *  p1  =   new   int ( 71 );

int *  p2  =  p1;  // p2和p1 現在指向同一內存地址

cout 
<<   * p1  <<  endl;

cout 
<<   * p2  <<  endl;

delete p1; 
// OK

delete p2; 
// ERROR! p2所指的內存,已通過delete p1而被釋放,不可再delete一次
同樣的問題,如果你先刪除了p2 ,則同樣不可再刪除p1
delete p2;  // OK

delete p1; 
// ERROR

第三種為刪除指向某一普通變量的指針
int  a  =   100 ;

int *  p  =   & a;

delete p; 
// ERROR 

p 不是通過new 得到新的內存空間,而是直接指向固定變量a。所以刪除p等同要強行剝奪a的固有空間,會導致出錯。

 

因為我需要釋放指向二維結構體指針的指針,為測試內存釋放情況,改寫程序。代碼如下:

C++代碼 復制代碼  收藏代碼
  1. #include<iostream>   
  2. #include<stdio.h>   
  3. using namespace std;   
  4.   
  5. void main()   
  6. {   
  7.     cout << "指針變量占4字節內存。按回車鍵開始分配404M內存..." << endl;   
  8.     int temp;   
  9.     getchar();   
  10.   
  11.     int x = 100;   
  12.     int y = 1024*1024;   
  13.   
  14.     char ***p = new char** [y];   
  15.   
  16.     for(int i=0; i<y; ++i)   
  17.     {   
  18.         p[i] = new char*[x];   
  19.     }   
  20.   
  21.     cout << "內存分配完畢,按回車鍵開始清理內存..." << endl;   
  22.     getchar();   
  23.   
  24.     for (int i=0; i<y; ++i)   
  25.     {   
  26.         delete []p[i];   
  27.     }   
  28.   
  29.     cout << "二位數組,第二維清理完畢,共清理內存400M,剩余4M未清理,按回車鍵繼續清理..." << endl;   
  30.     getchar();   
  31.   
  32.     delete []p;   
  33.     p = NULL;   //如果不對p賦值NULL,再次清理內存,將報錯   
  34.   
  35.     /*cout << "再次清理內存..." << endl;  
  36.     getchar();  
  37.  
  38.     delete []p;  
  39.     p = NULL;*/  
  40.   
  41.     cout << "內存清理完畢..." << endl;   
  42.     getchar();   
  43. }  
#include<iostream>
#include<stdio.h>
using namespace std;

void main()
{
	cout << "指針變量占4字節內存。按回車鍵開始分配404M內存..." << endl;
	int temp;
	getchar();

	int x = 100;
	int y = 1024*1024;

	char ***p = new char** [y];

	for(int i=0; i<y; ++i)
	{
		p[i] = new char*[x];
	}

	cout << "內存分配完畢,按回車鍵開始清理內存..." << endl;
	getchar();

	for (int i=0; i<y; ++i)
	{
		delete []p[i];
	}

	cout << "二位數組,第二維清理完畢,共清理內存400M,剩余4M未清理,按回車鍵繼續清理..." << endl;
	getchar();

	delete []p;
	p = NULL;	//如果不對p賦值NULL,再次清理內存,將報錯

	/*cout << "再次清理內存..." << endl;
	getchar();

	delete []p;
	p = NULL;*/

	cout << "內存清理完畢..." << endl;
	getchar();
}
 

下載資源  請到http://hzdiy.iteye.com/blog/736816


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM