浅谈C++内存分配及变长数组的动态分配

2020-01-06 15:40:02王旭

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

安装后,使用其中的gflags.exe工具打开PageHeap,

gflags -p /enable MainD.exe /full

重新使用VS用调试方式运行,很快就找到了出错位置,因为在某个静态函数中笔误导致

在编写稳定的服务器程序时,这个工具尤为有用。

第二部分 数组的动态分配及实例

一、动态分配二维数组的一般方法是这样:假设数组存的数据类型是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维,在循环分配第2维。假设二维数组是3×2的,每一句运行完后的内存情况如图所示(方格表示内存,xx表示随机数。下面是内存地址。当然,这个地址是个示意,事实不会分配到那的。):

第一句完后分配了3个内存单元

循环分配后,注意下面3段内存是不连续的。这样用下表p[n][m]操作数组没问题,如果整块内存操作就会有问题了。

原意是想把下面的3块6个内存单元清0,可是事与愿违,把从p开始后面6个内存单元清0了,p[]不能用了。p后面只有3个已分配的内存单元,却要操作6个,另外3个是未知区域。清了后面虚线的3块未知区域,这就很危险了,可能导致程序崩溃。

这样分配的内存需要循环释放。

对这个方法有一改进,如下:


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;
  }

这段代码解决了分配的空间不连续的问题。每一句运行完后的内存情况如图所示:

第一句和上面一样。

这6个内存单元是一次分配的,所以连续。

这个二维数组的数据首地址是p[0],p是第2维的索引首地址。所以如果要对二维数组进行整体的内存(缓冲区 buffer)操作,要以p[0]为操作对象的首地址。

到此,索引与对应的数据地址关联上了。这个二维数组既可以通过下表p[][]来操作,又可以操作缓冲区。操作缓冲区的函数比如memcpy,cfile的writehuge和readhuge使用起来很方便,省去了2次循环的麻烦。

至于释放,不必循环释放。因为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; 
 
//第一种方法,分配连续空间 
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); 
} 
 
 
 
//第二中方法,分配连续空间,C++的实现版, 
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了,蛋疼,不过对于自定义的数据类型,很有必要 
{ 
  for (int i=0; i<row; ++i) 
    for (int j=0; j<col; ++j) 
      arr[i][j].~T();//这是什么玩意儿?!模板析构?因为使用了new?所以用析构函数的delete? 
  if (arr != NULL) 
    free((void **)arr); 
} 
 
int _tmain(int argc, _TCHAR* argv[]) 
{ 
  //一维数组动态分配 
  //int n; 
  //cin>>n; 
  ////int *p=new int[n];//一维数组动态分配方法一 
  //int *p=(int*)malloc(n*sizeof(int));//一维数组动态分配方法二 
  //for(int i=0;i<n;i++) 
  // cin>>p[i]; 
  //cout<<endl; 
  //for(int i=0;i<n;i++) 
  // cout<<p[i]<<" "; 
 
  //二维变长数组的动态分配,本人喜欢这种方法,虽然空间不连续,但同样可以进行p[i][j]的寻址,为什么博客中特意写上面介绍的函数来实现还没找到太好的理由 
  //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]是连续的 
  //cout<<&p[0]<<"   "<<&p[0][0]<<"   "<<&p[0][1]<<endl;//p[0]!=p[0][0],但p[0][0],p[0][1]是连续的 
 
 
  ////C版本的,分配连续空间 
  //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);//注意模板函数怎么实现的 <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>