hengai 阅读(978) 评论(22)

  最近,经常看到在论坛的VC板块看到有人问有关于动态创建二维数组的人,自己准备写一篇出来。
  记得自己刚学习C的时候,就需要学习数组,不过在学校里,我最多就是学到了二维数组,并且自己使用的也绝大部分是一维数组。后来,快毕业的时候,程序中需要一个使用动态的二维数组,那时候没有老师可以请教,自己摸索着,竟然被我弄出来了一段代码,可以动态创建二维数组(这段代码有可能是不正确的,希望行家不要取笑,只需要提出意见就可以了):

#include <stdio.h>

#include <iostream.h>
 

const int ROW = 10;

const int COL = 12;

 

int main()

{

  int iRow=0, iCol=0;

  //创建数组

       cout<<"Create array"<<endl;

  int **ppint = new int*[ROW];

  for(iRow=0;iRow<ROW;iRow++){

    ppint[iRow] = new int[COL];

  }

  //进行计算

  cout<<"TODO sth."<<endl;

  for(iRow=0;iRow<ROW;iRow++){

    for(iCol=0;iCol<COL;iCol++){

      ppint[iRow][iCol] = iRow+iCol;

    }

  }

  //输出

  cout<<"use the array"<<endl;

  for(iRow=0;iRow<ROW;iRow++){

    for(iCol=0;iCol<COL;iCol++){

      cout<<"["<<iRow<<"]["<<iCol<<"]"<<ppint[iRow][iCol]<<";";

    }

    cout<<endl;

  }

  //删除

  cout<<endl<<"Now, delete the array"<<endl;

  for(iRow=0;iRow<ROW;iRow++){

    delete[] ppint[iRow];

  }

  delete[] ppint;
  ppint = NULL;

  //

  cout<<"Press return to exit......."<<endl;

  getchar();

  return 0;

}

  上面的代码,演示了怎样创建、使用以及删除一个二维数组。这段代码虽然是我刚刚写的,但是早已成型于2002年底。我不知道这段代码创建一个二维数组有什么错误,但是一直到现在我仍在使用。

------------------------------------------------------------------------------

  上面的代码,是我自己琢磨出来的。后来,2003年来到北京工作,一个偶然的机会,看到C/C++有一个模板库,支持动态创建二维数组与删除二维数组,再后来,又在网上看到有人提供了一个宏来创建二维数组。
  关于那个创建与删除二维数组的模板,这下子我忘记了,自己写了一个类似的(如果写错了,还是那句话:只需要贴出正确的就可以了,少量的批评也行,就是不要骂人了

#include <stdio.h>

#include <iostream.h>

 

template <class T>

T** CreateMatrix(int nRow, int nCol)

{

  T **ppT = new T*[nRow];

  for(int iRow = 0;iRow<nRow;iRow++){

    ppT[iRow] = new T[nCol];

  }

  return ppT;

}

 

template <class T>

void DeleteMatrix(T ***pppT, int nRow)

{

  for(int iRow=0; iRow<nRow; iRow++){

    delete[] (*pppT)[iRow];

  }

  delete[] (*pppT);

  (*pppT) = NULL;

}

 

const int ROW = 10;

const int COL = 12;

 

int main()

{

  int iRow=0, iCol=0;

  //创建数组

       cout<<"Create array"<<endl;

  int **ppint = CreateMatrix<int>(ROW, COL);

  //进行计算

  cout<<"TODO sth."<<endl;

  for(iRow=0;iRow<ROW;iRow++){

    for(iCol=0;iCol<COL;iCol++){

      ppint[iRow][iCol] = iRow+iCol;

    }

  }

  //输出

  cout<<"use the array"<<endl;

  for(iRow=0;iRow<ROW;iRow++){

    for(iCol=0;iCol<COL;iCol++){

      cout<<"["<<iRow<<"]["<<iCol<<"]"<<ppint[iRow][iCol]<<";";

    }

    cout<<endl;

  }

  //删除

  cout<<endl<<"Now, delete the array"<<endl;

  DeleteMatrix<int>(&ppint, ROW);

  //

  cout<<"Pres return to exit......."<<endl;

  getchar();

  return 0;

}
  经过我的测试,输出的结果是一样的。

------------------------------------------------------------------------------

  至于那个使用宏来生成二维数组,个人不推荐,所以这里也就不贴出来了(主要是自己手头没有,而自己又懒得不愿意写)。

------------------------------------------------------------------------------

题外话:自己本来想测试一下这段代码是否正确的(虽然在实际使用中是正确的),想把内存中的内容给分析一下,但是发现自己是在使看不懂二维数组在内存中的表示。如果有谁能够分析验证一下,hengai先在这里谢过了!


评论列表
周星星
re: 动态创建二维数组
很好!

但这并不是 二维数组 ,在某些语言中你一定看过 int[2,3] 和 int[2][3] 的区别,在C/C++中 int [2][3] 是 二维数组,而
int* a[2] = { new int[3], new int[3] } 还只是一维数组,区别在于二维数组的每一维都是一个数组,而不是指向一个数组的指针。

在位本质上,多维数组和一维数组是一致的,你可以重载operator[]来用一维数组模拟多维数组。
hengai
to 周星星
谢谢!
的确,这不算是二维数组,但是几乎可以满足二维数组的要求,也可以当作二维数组来使用。
二维数组,其实在内存中是连续的一段地址,而我这个实现的,并不是一个连续的内存段。
周星星
to hengai:
《Effective C++ 2/e》中说:别做和内建类型不一致的行为。
对于内建二维数组,比如int a[2][3],fwrite( a, sizeof(a), f )就写入文件,fread( a, sizeof(a), f )就读入文件。
不是说你的实现有错误,而是说它可能会成为后来错误的伏笔。
hengai
to 周星星
的确,按照我这样的实现方法,在写入文件时会发生错误。但是要考虑到什么场合使用什么样的方法。在我的要求中,对于二维数组,一般不会要求写入到文件,即使有写入到文件的地方,自己也会注意。
不过我可能说得不完善,至少需要提醒使用这段代码的人,在写入文件(或者按照内存偏移来取内容的人)会出错。

再次谢谢你提出的问题
anonymous
re: 动态创建二维数组
只要是用二维(行列)来读写存储体都是二维数组, 只不过在实现上分连续型的和邻接型的罢了.

但我觉得连续型的存取效率比邻接的高, 因为它是定列宽的. 而邻接比连续的灵活.
hengai
re: 动态创建二维数组
我认为都差不多吧,都是对内存进行操作。
不过“邻接比连续的灵活”有一定的道理,因为你有时候分配一块巨大的内存时出现失败的可能性比申请多块小内存的可能性大(不知道这句话是否正确)。
不过,如果是连续型的,我就可以通过下标的累加来访问下一个元素。比如有个数组 a[10][10],我可以通过 (&a[0][9])++来访问a[1][0](代码可能有误),这又体现出来了更多的灵活性
anonymous
re: 动态创建二维数组
底层寻址应该是不同的, 连续的应该是间接寻址, 邻接的应该是基址或变址的寻址方式.  定列宽的好处就是减少一个寄存器, 把它变为立即数.
hengai
to Panic
呵呵,我现在也在写一个能够实现连续地址的二维数组内,刚刚已经请教完了关于 [][] 操作符的重载问题
你代码中,使用GetAt(int nRow,int nCol)来获得,毕竟没有直接使用[][]方便,以及习惯。
顺便说一下,文中提到的那个宏,我偶然找到了

#define matrix_allocate(matrix, width, height, TYPE) {\
matrix = new TYPE* [height];\
for(int _i = 0; _i < height; _i++)\
matrix[_i] = new TYPE[width];\
}
#define matrix_delete(matrix, width, height){\
for(int _i = 0; _i < height; _i++)\
delete [] matrix[_i];\
delete [] matrix;\
matrix = 0;\
}
不过我保证正确性
清风雨
因为个人C++中不太喜欢动态二维数组
我想你实现动态二维数组也是一种奇怪的方法,因为就实现而已,自己也还能没有问题,所以急下来看评论。
第一个评论看到了,所以急于过来说上几句了。
1.平凡的new小块内存,效率低效,还会导致内存碎片。
2.建议你重载operator[][]用来模拟二维数组的功能
3.建议实现体内可以考虑STL容器,而不要不停的new(当然,如果你觉得自己能够做的更好,自己做也无妨)
清风雨
就另一个评论
如果你想杜绝 = 和 copy构造,可以不妨考虑private一下。
这样,就不好=和copy崩溃了。

或者实现自己意义上的=和copy,不过由于数组的=是指针地址copy,所以还是趋向建议private,以保持一直性、降低学习难度(个人是极度反对不一直的东西到处都是,每个都要特殊处理的)。
清风雨
re: to pAnic
你肯定晕死了。^_^

搂主兄弟,他是告诉你方法,而不是形式。你把GetAt换成operator[][]就一样了。
hengai
to 清风雨
我仅仅实现的就是一个模拟的二维数组,关于那些类似于 = 的操作符,我不需要重载。昨天本来想自己实现一个能够动态生成的二维数组类,结果因为自己的水平确实不行,一个晚上都没有实现。我的想法是:假设要求的二维数组为ROW*COL,那么我先在内存中分配一个 new T[ROW*COL];也就是说使用线形的方式存储一个二维数组。
记得以前看到过pAnic兄(好像是他),回答一个人的问题“关于 foo(char p[][])与 foo(char **p) 有什么不同),昨天晚上自己也始了一下,个人认为,这就是传递一个二维数组与双重指针,如果我是使用 char p[10][12]那么只能是以 foo(char p[][])这种形式传递,如果这个 char p[][] 是按照我上面贴子所生成的方法,那么就需要使用 foo(char **p),两者之间绝对不能混用。
另外,我昨天晚上就是在重载[][]操作符上碰到了问题,我知道,如果我使用 GetAt 函数,可以很容易的解决这个问题,但是我需要的就是按照习惯,因为按照使用习惯,人们一般都是使用 p[row][col]来访问二维数组而不是使用 GetAt(row, col)来访问。

另外,我有一点说错了,这里自我批评:“我以为多次new一段小内存的效率会高于一次性new大段的内存”,这句话是错的。以前看过一篇文章,说“如果需要频繁的分配内存,可以先使用HeapAlloc来一大段的内存,然后在从这里划分”。再次自我批评一下。
blueskyzsz
re: 动态创建二维数组
为什么不用boost 的多维数组? 或者看看它的实现方式?
hengai
to 清风雨
就是不想用那么高级的东西,所以才写这些的,要不然我直接使用 vctor< vctor<T> > 了。
有点问题吧
re: 动态创建二维数组
看了半天,没有看懂,主要唵不是学这个的,呵呵,
但是觉得还是有点问题,比如第一算法,那那里是动态的,还不是静态的,因为有两个CONST呀,

发表评论
切换编辑模式