亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

淺談C++內存分配及變長數(shù)組的動態(tài)分配

 更新時間:2016年09月04日 12:02:23   投稿:jingxian  
下面小編就為大家?guī)硪黄獪\談C++內存分配及變長數(shù)組的動態(tài)分配。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

第一部分 C++內存分配

一。關于內存

1、內存分配方式

內存分配方式有三種:

(1)從靜態(tài)存儲區(qū)域分配。內存在程序編譯的時候就已經(jīng)分配好,這塊內存在程序的整個運行期間都存在

例如全局變量,static變量。

(2)在棧上創(chuàng)建。在執(zhí)行函數(shù)時,函數(shù)內局部變量的存儲單元都可以在棧上創(chuàng)建,函數(shù)執(zhí)行結束時這些存

儲單元自動被釋放。棧內存分配運算內置于處理器的指令集中,效率很高,但是分配的內存容量有限。

(3) 從堆上分配,亦稱動態(tài)內存分配。程序在運行的時候用malloc或new申請任意多少的內存,程序員自

己負責在何時用free或delete釋放內存。動態(tài)內存的生存期由我們決定,使用非常靈活,但問題也最多。

2.內存使用錯誤

發(fā)生內存錯誤是件非常麻煩的事情。編譯器不能自動發(fā)現(xiàn)這些錯誤,通常是在程序運行時才能捕捉到。

而這些錯誤大多沒有明顯的癥狀,時隱時現(xiàn),增加了改錯的難度。有時用戶怒氣沖沖地把你找來,程序卻沒有

發(fā)生任何問題,你一走,錯誤又發(fā)作了。 常見的內存錯誤及其對策如下:

* 內存分配未成功,卻使用了它。

編程新手常犯這種錯誤,因為他們沒有意識到內存分配會不成功。常用解決辦法是,在使用內存之前檢查

指針是否為NULL。如果是用malloc或new來申請內存,應該用if(p==NULL) 或if(p!=NULL)進行防錯處理。

* 內存分配雖然成功,但是尚未初始化就引用它。

犯這種錯誤主要有兩個起因:一是沒有初始化的觀念;二是誤以為內存的缺省初值全為零,導致引用初值

錯誤(例如數(shù)組)。 內存的缺省初值究竟是什么并沒有統(tǒng)一的標準,盡管有些時候為零值,我們寧可信其無不

可信其有。所以無論用何種方式創(chuàng)建數(shù)組,都別忘了賦初值,即便是賦零值也不可省略,不要嫌麻煩。

* 內存分配成功并且已經(jīng)初始化,但操作越過了內存的邊界。

例如在使用數(shù)組時經(jīng)常發(fā)生下標“多1”或者“少1”的操作。特別是在for循環(huán)語句中,循環(huán)次數(shù)很容易搞錯,導致數(shù)組操作越界。

* 忘記了釋放內存,造成內存泄露。

含有這種錯誤的函數(shù)每被調用一次就丟失一塊內存。剛開始時系統(tǒng)的內存充足,你看不到錯誤。終有一次

程序突然死掉,系統(tǒng)出現(xiàn)提示:內存耗盡。

動態(tài)內存的申請與釋放必須配對,程序中malloc與free的使用次數(shù)一定要相同,否則肯定有錯誤

(new/delete同理)。

* 釋放了內存卻繼續(xù)使用它。

有三種情況:

(1)程序中的對象調用關系過于復雜,實在難以搞清楚某個對象究竟是否已經(jīng)釋放了內存,此時應該重新

設計數(shù)據(jù)結構,從根本上解決對象管理的混亂局面。

(2)函數(shù)的return語句寫錯了,注意不要返回指向“棧內存”的“指針”或者“引用”,因為該內存在函

數(shù)體結束時被自動銷毀。

(3)使用free或delete釋放了內存后,沒有將指針設置為NULL。導致產(chǎn)生“野指針”。

【規(guī)則1】用malloc或new申請內存之后,應該立即檢查指針值是否為NULL。防止使用指針值為NULL的內存

【規(guī)則2】不要忘記為數(shù)組和動態(tài)內存賦初值。防止將未被初始化的內存作為右值使用。

【規(guī)則3】避免數(shù)組或指針的下標越界,特別要當心發(fā)生“多1”或者“少1”操作。

【規(guī)則4】動態(tài)內存的申請與釋放必須配對,防止內存泄漏。

【規(guī)則5】用free或delete釋放了內存之后,立即將指針設置為NULL,防止產(chǎn)生“野指針”。

二. 詳解new,malloc,GlobalAlloc
   
1.  new

new和delete運算符用于動態(tài)分配和撤銷內存的運算符

new用法:

1> 開辟單變量地址空間

1)new int;  //開辟一個存放數(shù)組的存儲空間,返回一個指向該存儲空間的地址.int *a = new

int 即為將一個int類型的地址賦值給整型指針a.

2)int *a = new int(5) 作用同上,但是同時將整數(shù)賦值為5

2> 開辟數(shù)組空間

一維: int *a = new int[100];開辟一個大小為100的整型數(shù)組空間

一般用法: new 類型 [初值]

delete用法:

1> int *a = new int;

delete a;   //釋放單個int的空間

2>int *a = new int[5];

delete [] a; //釋放int數(shù)組空間

要訪問new所開辟的結構體空間,無法直接通過變量名進行,只能通過賦值的指針進行訪問.

用new和delete可以動態(tài)開辟,撤銷地址空間.在編程序時,若用完一個變量(一般是暫時存儲的數(shù)組),

下次需要再用,但卻又想省去重新初始化的功夫,可以在每次開始使用時開辟一個空間,在用完后撤銷它.

2.  malloc

原型:extern void *malloc(unsigned int num_bytes);

用法:#i nclude <malloc.h>或#i nclude <stdlib.h>

功能:分配長度為num_bytes字節(jié)的內存塊

說明:如果分配成功則返回指向被分配內存的指針,否則返回空指針NULL。

當內存不再使用時,應使用free()函數(shù)將內存塊釋放。

malloc的語法是:指針名=(數(shù)據(jù)類型*)malloc(長度),(數(shù)據(jù)類型*)表示指針.

說明:malloc 向系統(tǒng)申請分配指定size個字節(jié)的內存空間。返回類型是 void* 類型。void* 表示未確定類型

的指針。C,C++規(guī)定,void* 類型可以強制轉換為任何其它類型的指針。

malloc()函數(shù)的工作機制

malloc函數(shù)的實質體現(xiàn)在,它有一個將可用的內存塊連接為一個長長的列表的所謂空閑鏈表。調用malloc

函數(shù)時,它沿連接表尋找一個大到足以滿足用戶請求所需要的內存塊。然后,將該內存塊一分為二(一塊的大

小與用戶請求的大小相等,另一塊的大小就是剩下的字節(jié))。接下來,將分配給用戶的那塊內存?zhèn)鹘o用戶,并

將剩下的那塊(如果有的話)返回到連接表上。調用free函數(shù)時,它將用戶釋放的內存塊連接到空閑鏈上。到

最后,空閑鏈會被切成很多的小內存片段,如果這時用戶申請一個大的內存片段,那么空閑鏈上可能沒有可以

滿足用戶要求的片段了。于是,malloc函數(shù)請求延時,并開始在空閑鏈上翻箱倒柜地檢查各內存片段,對它們

進行整理,將相鄰的小空閑塊合并成較大的內存塊。
 
和new的不同

從函數(shù)聲明上可以看出。malloc 和 new 至少有兩個不同: new 返回指定類型的指針,并且可以自動計算所需

要大小。比如:

int *p;
p = new int; //返回類型為int* 類型(整數(shù)型指針),分配大小為 sizeof(int);

或:
int* parr;
parr = new int [100]; //返回類型為 int* 類型(整數(shù)型指針),分配大小為 sizeof(int) * 100;

而 malloc 則必須由我們計算要字節(jié)數(shù),并且在返回后強行轉換為實際類型的指針。
int* p;
p = (int *) malloc (sizeof(int));

第一、malloc 函數(shù)返回的是 void * 類型,如果你寫成:p = malloc (sizeof(int)); 則程序無法通過編譯,

報錯:“不能將 void* 賦值給 int * 類型變量”。所以必須通過 (int *) 來將強制轉換。

第二、函數(shù)的實參為 sizeof(int) ,用于指明一個整型數(shù)據(jù)需要的大小。如果你寫成:
int* p = (int *) malloc (1);

代碼也能通過編譯,但事實上只分配了1個字節(jié)大小的內存空間,當你往里頭存入一個整數(shù),就會有3個字節(jié)無

家可歸,而直接“住進鄰居家”!造成的結果是后面的內存中原有數(shù)據(jù)內容全部被清空。

3.  GlobalAlloc
 
VC中關于GlobalAlloc,GlobalLock,GlobalUnLock

調用GlobalAlloc函數(shù)分配一塊內存,該函數(shù)會返回分配的內存句柄。

調用GlobalLock函數(shù)鎖定內存塊,該函數(shù)接受一個內存句柄作為參數(shù),然后返回一個指向被鎖定的內存塊的指針。 您可以用該指針來讀寫內存。

調用GlobalUnlock函數(shù)來解鎖先前被鎖定的內存,該函數(shù)使得指向內存塊的指針無效。

調用GlobalFree函數(shù)來釋放內存塊。您必須傳給該函數(shù)一個內存句柄。
 
GlobalAlloc

說明

分配一個全局內存塊

返回值

Long,返回全局內存句柄。零表示失敗。會設置GetLastError

參數(shù)表

參數(shù) 類型及說明

wFlags Long,對分配的內存類型進行定義的常數(shù)標志,如下所示:
GMEM_FIXED 分配一個固定內存塊
GMEM_MOVEABLE 分配一個可移動內存塊
GMEM_DISCARDABLE 分配一個可丟棄內存塊
GMEM_NOCOMPACT 堆在這個函數(shù)調用期間不進行累積
GMEM_NODISCARD 函數(shù)調用期間不丟棄任何內存塊
GMEM_ZEROINIT 新分配的內存塊全部初始化成零
dwBytes Long,要分配的字符數(shù)

GlobalLock 

函數(shù)功能描述:鎖定一個全局的內存對象,返回指向該對象的第一個字節(jié)的指針

函數(shù)原型:

LPVOID GlobalLock( HGLOBAL hMem )

參數(shù):

hMem:全局內存對象的句柄。這個句柄是通過GlobalAlloc或GlobalReAlloc來得到的

返回值:

調用成功,返回指向該對象的第一個字節(jié)的指針

調用失敗,返回NULL,可以用GetLastError來獲得出錯信息

注意:

調用過GlobalLock鎖定一塊內存區(qū)后,一定要調用GlobalUnlock來解鎖
 
GlobalUnlock

函數(shù)功能描述:解除被鎖定的全局內存對象

函數(shù)原型:BOOL GlobalUnlock( HGLOBAL hMem );

參數(shù):hMem:全局內存對象的句柄

返回值:

非零值,指定的內存對象仍處于被鎖定狀態(tài)0,函數(shù)執(zhí)行出錯,可以用GetLastError來獲得出錯信息,如果返回NO_ERROR,則表示內存對象已經(jīng)解鎖了

注意:這個函數(shù)實際上是將內存對象的鎖定計數(shù)器減一,如果計數(shù)器不為0,則表示執(zhí)行過多個GlobalLock

函數(shù)來對這個內存對象加鎖,需要對應數(shù)目的GlobalUnlock函數(shù)來解鎖。如果通過GetLastError函數(shù)返回錯誤

碼為ERROR_NOT_LOCKED,則表示未加鎖或已經(jīng)解鎖。

示例:

// Malloc memory
hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, nSize);
// Lock memory
pMem = (BYTE *) GlobalLock(hMem);
..................
// Unlock memory
GlobalUnlock(hMem);
GlobalFree(hMem);

三 總結

靈活自由是C/C++語言的一大特色,而這也為C/C++程序員出了一個難題。當程序越來越復雜時,內存的管理也會變得越加復雜,稍有不慎就會出現(xiàn)內存問 題。內存泄漏是最常見的內存問題之一。內存泄漏如果不是很嚴重,在短時間內對程序不會有太大的影響,這也使得內存泄漏問題有很強的隱蔽性,不容易被發(fā)現(xiàn)。 然而不管內

存泄漏多么輕微,當程序長時間運行時,其破壞力是驚人的,從性能下降到內存耗盡,甚至會影響到其他程序的正常運行。另外內存問題的一個共同特點 是,內存問題本身并不會有很明顯的現(xiàn)象,當有異常現(xiàn)象出現(xiàn)時已時過境遷,其現(xiàn)場已非出現(xiàn)問題時的現(xiàn)場了,這給調試內存問題帶來了很大的難度。

下載Windows Debug 工具, http://www.microsoft.com/whdc/devtools/debugging/default.mspx

安裝后,使用其中的gflags.exe工具打開PageHeap,

gflags -p /enable MainD.exe /full

重新使用VS用調試方式運行,很快就找到了出錯位置,因為在某個靜態(tài)函數(shù)中筆誤導致

在編寫穩(wěn)定的服務器程序時,這個工具尤為有用。

第二部分 數(shù)組的動態(tài)分配及實例

一、動態(tài)分配二維數(shù)組的一般方法是這樣:假設數(shù)組存的數(shù)據(jù)類型是int

int **p=NULL; 
p=new int*[nWidth];
  if (!p){
    return NULL;
  }
  for (int j=0;j<nWidth;j++){
    p[j]=new int[nHeight];
    if (!p[j]){
      return NULL;
    }
  }

這段代碼淺顯易懂,先分配第1維,在循環(huán)分配第2維。假設二維數(shù)組是3×2的,每一句運行完后的內存情況如圖所示(方格表示內存,xx表示隨機數(shù)。下面是內存地址。當然,這個地址是個示意,事實不會分配到那的。):

第一句完后分配了3個內存單元

循環(huán)分配后,注意下面3段內存是不連續(xù)的。這樣用下表p[n][m]操作數(shù)組沒問題,如果整塊內存操作就會有問題了。

原意是想把下面的3塊6個內存單元清0,可是事與愿違,把從p開始后面6個內存單元清0了,p[]不能用了。p后面只有3個已分配的內存單元,卻要操作6個,另外3個是未知區(qū)域。清了后面虛線的3塊未知區(qū)域,這就很危險了,可能導致程序崩潰。

這樣分配的內存需要循環(huán)釋放。

對這個方法有一改進,如下:

int **p=NULL; 
  p=new int *[nWidth];
if (!p){
    return NULL;
  }
  p[0]=new int[nWidth*nHeight];
if (!p[0]){
  delete[] p;
    return NULL;
  }
  ZeroMemory(p[0],nWidth*nHeight*sizeof(int));
  for (int i=1;i<nWidth;i++){
    p[i]=p[i-1]+nHeight;
  }

這段代碼解決了分配的空間不連續(xù)的問題。每一句運行完后的內存情況如圖所示:

第一句和上面一樣。

這6個內存單元是一次分配的,所以連續(xù)。

這個二維數(shù)組的數(shù)據(jù)首地址是p[0],p是第2維的索引首地址。所以如果要對二維數(shù)組進行整體的內存(緩沖區(qū) buffer)操作,要以p[0]為操作對象的首地址。

到此,索引與對應的數(shù)據(jù)地址關聯(lián)上了。這個二維數(shù)組既可以通過下表p[][]來操作,又可以操作緩沖區(qū)。操作緩沖區(qū)的函數(shù)比如memcpy,cfile的writehuge和readhuge使用起來很方便,省去了2次循環(huán)的麻煩。

至于釋放,不必循環(huán)釋放。因為new了2次,所以只需delete2次就行了:

if(!p){
  return;
}
  delete []p[0];
  p[0]=NULL;
  delete[] p;
  p=NULL;

二  實例

<span style="font-size:14px;">// malloc2d.cpp : Defines the entry point for the console application. 
// 
 
#include "stdafx.h" 
#include <iostream> 
#include <stdlib.h> 
#include <string.h> 
using namespace std; 
 
//第一種方法,分配連續(xù)空間 
void **malloc2d(int row,int col,int size) 
{ 
  void **arr; 
  int indexsize=sizeof(void*)*row;//空出indexsize大小的空間用作? void*為什么不行? 
  int totalsize=size*row*col; 
  arr=(void**)malloc(indexsize+totalsize); 
  if(arr!=NULL) 
  { 
    unsigned char *head;//博客中是void *head版本,但編譯都通過不了,改成unsigned char* 后編譯通過,但不明白運行結果為什么不對 
    head=(unsigned char *)arr+indexsize; 
    memset(arr,0,indexsize+totalsize); 
    for(int i=0;i<row;i++) 
      arr[i]=head+size*i*col; 
  } 
  return arr; 
} 
 
void free2d(void **arr) 
{ 
  if(arr!=NULL) 
    free(arr); 
} 
 
 
 
//第二中方法,分配連續(xù)空間,C++的實現(xiàn)版, 
template <typename T> 
T **darray_new(int row, int col) 
{ 
  int size=sizeof(T); 
  void **arr=(void **) malloc(sizeof(void *) * row + size * row * col); 
  if (arr != NULL) 
  { 
    unsigned char * head; 
    head=(unsigned char *) arr + sizeof(void *) * row; 
    for (int i=0; i<row; ++i) 
    { 
      arr[i]= head + size * i * col; 
      for (int j=0; j<col; ++j) 
        new (head + size * (i * col + j)) T;//這一句比較有意思,想一想為什么? 
    } 
  } 
  return (T**) arr; 
} 
 
template <typename T> 
void darray_free(T **arr, int row, int col)//注意要一個一個delete了,蛋疼,不過對于自定義的數(shù)據(jù)類型,很有必要 
{ 
  for (int i=0; i<row; ++i) 
    for (int j=0; j<col; ++j) 
      arr[i][j].~T();//這是什么玩意兒?!模板析構?因為使用了new?所以用析構函數(shù)的delete? 
  if (arr != NULL) 
    free((void **)arr); 
} 
 
int _tmain(int argc, _TCHAR* argv[]) 
{ 
  //一維數(shù)組動態(tài)分配 
  //int n; 
  //cin>>n; 
  ////int *p=new int[n];//一維數(shù)組動態(tài)分配方法一 
  //int *p=(int*)malloc(n*sizeof(int));//一維數(shù)組動態(tài)分配方法二 
  //for(int i=0;i<n;i++) 
  // cin>>p[i]; 
  //cout<<endl; 
  //for(int i=0;i<n;i++) 
  // cout<<p[i]<<" "; 
 
  //二維變長數(shù)組的動態(tài)分配,本人喜歡這種方法,雖然空間不連續(xù),但同樣可以進行p[i][j]的尋址,為什么博客中特意寫上面介紹的函數(shù)來實現(xiàn)還沒找到太好的理由 
  //int n; 
  //cin>>n; 
  //int *p[2]; 
  //p[0]=new int[n]; 
  //p[1]=new int[n+1]; 
  //for(int i=0;i<n;i++) 
  // cin>>p[0][i]; 
  //cout<<&p[0]<<"   "<<&p[1]<<endl;//p[0],p[1]是連續(xù)的 
  //cout<<&p[0]<<"   "<<&p[0][0]<<"   "<<&p[0][1]<<endl;//p[0]!=p[0][0],但p[0][0],p[0][1]是連續(xù)的 
 
 
  ////C版本的,分配連續(xù)空間 
  //int**m=(int**)malloc2d(5,5,sizeof(int)); 
  //int i,j; 
  //for( i=0;i<5;i++)              //void* 泛型指針,有待剖析 
  // for( j=0;j<5;j++) 
  //   m[i][j]=0; 
  //for( i=0;i<5;i++) 
  //{ 
  // for( j=0;j<5;j++) 
  //   cout<<m[i][j]<<" "; 
  // cout<<endl; 
  //} 
  //free2d((void**)m); 
 
 
  int** m=darray_new<int>(5,5);//注意模板函數(shù)怎么實現(xiàn)的 <int>! 
  int i,j; 
  for( i=0;i<5;i++) 
    for( j=0;j<5;j++) 
      m[i][j]=1; 
  for( i=0;i<5;i++) 
  { 
    for( j=0;j<5;j++) 
      cout<<m[i][j]<<" "; 
    cout<<endl; 
  } 
  darray_free(m,5,5); 
  return 0; 
} 
</span> 

以上這篇淺談C++內存分配及變長數(shù)組的動態(tài)分配就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關文章

  • 用C++編寫擴展node.js(node-ffi版)

    用C++編寫擴展node.js(node-ffi版)

    今天小編就為大家分享一篇關于用C++編寫擴展node.js(node-ffi版),小編覺得內容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2018-12-12
  • C++實現(xiàn)簡單BP神經(jīng)網(wǎng)絡

    C++實現(xiàn)簡單BP神經(jīng)網(wǎng)絡

    這篇文章主要為大家詳細介紹了C++實現(xiàn)簡單BP神經(jīng)網(wǎng)絡,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-05-05
  • c++實現(xiàn)跳躍表(Skip List)的方法示例

    c++實現(xiàn)跳躍表(Skip List)的方法示例

    跳表(skiplist)是一個非常優(yōu)秀的數(shù)據(jù)結構,實現(xiàn)簡單,插入、刪除、查找的復雜度均為O(logN),下面這篇文章主要介紹了c++實現(xiàn)跳躍表(Skip List)的相關資料,需要的朋友可以參考借鑒,下面隨著小編來一起學習學習吧。
    2017-09-09
  • C++算法學習之貪心算法的應用

    C++算法學習之貪心算法的應用

    貪心算法是指,在對問題求解時,總是做出在當前看來是最好的選擇。本文為大家準備了幾個示例,從而能深入了解貪心算法的應用,需要的可以參考一下
    2022-05-05
  • 用C++實現(xiàn),將一句話里的單詞進行倒置的方法詳解

    用C++實現(xiàn),將一句話里的單詞進行倒置的方法詳解

    本篇文章是對用C++實現(xiàn),將一句話里的單詞進行倒置的方法進行了詳細的分析介紹,需要的朋友參考下
    2013-05-05
  • C語言驅動開發(fā)之內核解鎖與強刪文件

    C語言驅動開發(fā)之內核解鎖與強刪文件

    在某些時候我們的系統(tǒng)中會出現(xiàn)一些無法被正常刪除的文件,如果想要強制刪除則需要在驅動層面對其進行解鎖后才可刪掉,本文為大家介紹了內核解鎖與強刪文件的方法,希望對大家有所幫助
    2023-06-06
  • C++中整形與浮點型如何在內存中的存儲詳解

    C++中整形與浮點型如何在內存中的存儲詳解

    大家好!這期和大家分享整形和浮點型是如何在數(shù)據(jù)是如何在內存中存儲,下面文章具有一定的參考價值,需要的小伙伴可以參考一下
    2022-05-05
  • Java?C++?算法題解leetcode145商品折扣后最終價格單調棧

    Java?C++?算法題解leetcode145商品折扣后最終價格單調棧

    這篇文章主要介紹了Java?C++?算法題解leetcode145商品折扣后最終價格單調棧示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-09-09
  • javascript 兩種聲明函數(shù)的方式的分析

    javascript 兩種聲明函數(shù)的方式的分析

    這篇文章主要介紹了javascript 兩種聲明函數(shù)的方式的分析的相關資料,需要的朋友可以參考下
    2017-02-02
  • Qt項目實戰(zhàn)之實現(xiàn)MP3音樂播放器

    Qt項目實戰(zhàn)之實現(xiàn)MP3音樂播放器

    這篇文章主要為大家詳細介紹了如何利用Qt實現(xiàn)MP3音樂播放器,文中的示例代碼講解詳細,具有一定的參考價值,感興趣的小伙伴可以了解一下
    2023-03-03

最新評論