【数组与指针】如何在C/C++中动态分配二维数组 .

MoreWindows
《白话经典算法》、《秒杀多线程》作者
博客专家认证
2013-01-05 03:57:11
【数组与指针】如何在C/C++中动态分配二维数组
在C/C++中动态分配二维数组可以先申请一维的指针数组,然后该数组中的每个指针再申请数组,这样就相当于二维数组了,但是这种方法会导致每行可能不相邻,从而访问效率比较低。如何申请连续的二维数组了?本文将分别三个方面讲解:
一.动态申请列大小固定的二维数组
二.C语言中动态申请连续的二维数组
三.C++语言中动态申请连续的二维数组



一.动态申请列大小固定的二维数组
首先如果二维数组的列大小固定,那么很简单,可以用申请一维数数组再其指针强制转化成为二维数组指针即可。详见代码:

//列大小固定的二维数组可以申请一维数据并将指针强转成二维数组
//By MoreWindows-(http://blog.csdn.net/MoreWindows)
#include <stdio.h>
int main()
{
printf(" 列大小固定的二维数组可以申请一维数据并将指针强转成二维数组\n");
printf(" -- by MoreWindows( http://blog.csdn.net/MoreWindows ) --\n\n");

//列值固定
const int MAXCOL = 3;

int nRow;
printf("请输入二维数组的行数(列值固定为%d): ", MAXCOL);
scanf("%d", &nRow);

//申请一维数据并将其转成二维数组指针
int *pp_arr = new int[nRow * MAXCOL];
int (*p)[MAXCOL] = (int(*)[MAXCOL])pp_arr;

//为二维数组赋值
int i, j;
for (i = 0; i < nRow; i++)
for (j = 0; j < MAXCOL; j++)
p[i][j] = i + j;

//输出二维数组
for (i = 0; i < nRow; i++)
{
for (j = 0; j < MAXCOL; j++)
printf("%5d", p[i][j]);
putchar('\n');
}

//释放资源
delete[] pp_arr;
return 0;
}

运行结果如下所示:




二.C语言中动态申请连续的二维数组
上面的方法虽然方便,但必须要求列的大小固定。下面先来试下在C语言中如何动态申请连续的二维数组。可以采用多申请一些指针,然后这一些指针分别指向后面数据区中对应的位置,如一个3*4的int类型数组,我们先申请大小为sizeof(int*) * 3 + 3 * 4 * sizeof(int)的一维数组设为arr。然后arr[0]存放指向arr + sizeof(int*) * 3这个位置的指针,arr[1]存放指向arr + sizeof(int*) * 3 + 4 * sizeof(int)这个位置的指针, arr[2]存放指向arr + sizeof(int*) * 3 + 2 * 4 * sizeof(int)这个位置的指针。下面用图展示指向的示意:



详细代码如下,由于指针操作有点小复杂,请读者耐心看:

//C语言中动态的申请二维数组 malloc free
//By MoreWindows-(http://blog.csdn.net/MoreWindows)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//动态申请二维数组
template <typename T>
T** malloc_Array2D(int row, int col)
{
int size = sizeof(T);
int point_size = sizeof(T*);
//先申请内存,其中point_size * row表示存放row个行指针
T **arr = (T **) malloc(point_size * row + size * row * col);
if (arr != NULL)
{
memset(arr, 0, point_size * row + size * row * col);
T *head = (T*)((int)arr + point_size * row);
while (row--)
arr[row] = (T*)((int)head + row * col * size);
}
return (T**)arr;
}
//释放二维数组
void free_Aarray2D(void **arr)
{
if (arr != NULL)
free(arr);
}
int main()
{
printf(" C语言中动态的申请二维数组 malloc free\n");
printf(" -- by MoreWindows( http://blog.csdn.net/MoreWindows ) --\n\n");

printf("请输入行列(以空格分开): ");
int nRow, nCol;
scanf("%d %d", &nRow, &nCol);

//动态申请连续的二维数组
int **p = malloc_Array2D<int>(nRow, nCol);

//为二维数组赋值
int i, j;
for (i = 0; i < nRow; i++)
for (j = 0; j < nCol; j++)
p[i][j] = i + j;

//输出二维数组
for (i = 0; i < nRow; i++)
{
for (j = 0; j < nCol; j++)
printf("%4d ", p[i][j]);
putchar('\n');
}

free_Aarray2D((void**)p);
return 0;
}

运行结果如下:



三.C++语言中动态申请连续的二维数组
可以看出我们已经成功实现了在C语言中动态申请连续的二维数组,如果上面的程序不使用int类型而使用string类这种类型,那会有什么后果了?肯定的说,由于没有调用构造函数和析构函数,程序绝对会造成内存泄露。因此要做下改进,下面给出在C++语言中动态申请连续的二维数组的代码,有些C++语法可能平时见得少,但其实这些语法在STL里面运用还是比较多的,有兴趣的童鞋应该掌握下。

//C++语言中动态的申请二维数组 new delete
//By MoreWindows-(http://blog.csdn.net/MoreWindows)
#include <new>
#include <cstdio>
#include <cstdlib>
#include <string>
using namespace std;
//动态申请二维数组
template <typename T>
T** new_Array2D(int row, int col)
{
int size = sizeof(T);
int point_size = sizeof(T*);
//先申请内存,其中sizeof(T*) * row表示存放row个行指针
T **arr = (T **) malloc(point_size * row + size * row * col);
if (arr != NULL)
{
T *head = (T*)((int)arr + point_size * row);
for (int i = 0; i < row; ++i)
{
arr[i] = (T*)((int)head + i * col * size);
for (int j = 0; j < col; ++j)
new (&arr[i][j]) T;
}
}
return (T**)arr;
}
//释放二维数组
template <typename T>
void delete_Array2D(T **arr, int row, int col)
{
for (int i = 0; i < row; ++i)
for (int j = 0; j < col; ++j)
arr[i][j].~T();
if (arr != NULL)
free((void**)arr);
}
int main()
{
printf(" C++语言中动态的申请二维数组 new delete\n");
printf(" -- by MoreWindows( http://blog.csdn.net/MoreWindows ) --\n\n");

printf("请输入行列(以空格分开): ");
int nRow, nCol;
scanf("%d %d", &nRow, &nCol);

//动态申请连续的二维数组
string **p = new_Array2D<string>(nRow, nCol);

//为二维数组赋值
int i, j;
for (i = 0; i < nRow; i++)
for (j = 0; j < nCol; j++)
{
char szTemp[30];
sprintf(szTemp, "(第%d行,第%d列)", i, j);
p[i][j] = szTemp;
}

//输出二维数组
for (i = 0; i < nRow; i++)
{
for (j = 0; j < nCol; j++)
printf("%s ", p[i][j].c_str());
putchar('\n');
}

delete_Array2D<string>(p, nRow, nCol);
return 0;
}


运行结果如下:




本贴对应博客文章
http://blog.csdn.net/morewindows/article/details/7664479

更多C/C++/C#基础知识文章请访问:
http://blog.csdn.net/morewindows/article/category/868661

更多Windows编程文章请访问:
http://blog.csdn.net/MoreWindows


欢迎大家讨论交流,回贴必给分。

...全文
1346 15 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
15 条回复
切换为时间正序
请发表友善的回复…
发表回复
oCrash123 2014-08-22
  • 打赏
  • 举报
回复
好吧我漏看了。。 换成 BYTE **p = malloc_Array2D<BYTE>(nRow, nCol); 就可以用了。 谢谢楼主的方法!
oCrash123 2014-08-22
  • 打赏
  • 举报
回复
谢谢楼主!对我帮助很大! 我有个小问题问一下楼主,我想生成m行n列的动态二维数组,每个数的类型是BYTE,套用 int **p = malloc_Array2D<BYTE>(nRow, nCol); 出现错误error C2440: “初始化”: 无法从“BYTE **”转换为“int **” 1> 与指向的类型无关;转换要求 reinterpret_cast、C 样式转换或函数样式转换 请问为什么。这种方法能不能定义BYTE类型的二维数组?
xzyxuexi1 2014-04-26
  • 打赏
  • 举报
回复
谢谢楼主的分享,对于new (&arr[i][j]) T;存有疑问?请问楼主原意是想给arr[i][j]赋值为0呢?那是不是写错了呢?得改成new (&arr[i][j]) T(0);呢?
蓝鹰 2013-03-20
  • 打赏
  • 举报
回复
c++用std::sting 的那个应该不是连续的吧
赵4老师 2013-01-06
  • 打赏
  • 举报
回复
引用 9 楼 syrchina 的回复:
引用 5 楼 zhao4zhong1 的回复:VC调试时按Alt+8e…… 您干什么这是。。。到处粘贴蹭分?
楼主如果觉得这是蹭分可以不给分,还可以删掉。
zhcosin 2013-01-05
  • 打赏
  • 举报
回复
我觉得那个不连续的内存效率也低不到哪里去吧,我从来就是这么干的,见 http://blog.csdn.net/zhcosin/article/details/8275603
rickys2080 2013-01-05
  • 打赏
  • 举报
回复
引用 5 楼 zhao4zhong1 的回复:
VC调试时按Alt+8e……
您干什么这是。。。到处粘贴蹭分?
baichi4141 2013-01-05
  • 打赏
  • 举报
回复
真心不理解多维数组有哪里好……
lin5161678 2013-01-05
  • 打赏
  • 举报
回复
引用 6 楼 MoreWindows 的回复:
这就是文章中的 一.动态申请列大小固定的二维数组
是啊 我发现了 嘿嘿
MoreWindows 2013-01-05
  • 打赏
  • 举报
回复
引用 4 楼 lin5161678 的回复:
申请足够大的内存 然后用特定的数组指针指向他 就有这样的效果了 比如 申请 int[5][8] 这样的动态二维数组 int (*pArr)[8] = (int(*)[8]) malloc(sizeof(int[5][8])); 然后 pArr 就可以当成一个二维数组int[5][8]使用了 pArr[3][4]=100;
这就是文章中的 一.动态申请列大小固定的二维数组
赵4老师 2013-01-05
  • 打赏
  • 举报
回复
VC调试时按Alt+8、Alt+6和Alt+5,打开汇编窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应内存和寄存器变化,这样过一遍不就啥都明白了吗。 对VC来说,所谓‘调试时’就是编译连接通过以后,按F10或F11键单步执行一步以后的时候,或者在某行按F9设了断点后按F5执行停在该断点处的时候。 (Turbo C或Borland C用Turbo Debugger调试,Linux或Unix下用GDB调试时,看每句C对应的汇编并单步执行观察相应内存和寄存器变化。) 想要从本质上理解C指针,必须学习汇编以及C和汇编的对应关系。 从汇编的角度理解和学习C语言的指针,原本看似复杂的东西就会变得非常简单! 指针即地址。“地址又是啥?”“只能从汇编语言和计算机组成原理的角度去解释了。” 但我又不得不承认: 有那么些人喜欢或者适合用“先具体再抽象”的方法学习和理解复杂事物; 而另一些人喜欢或者适合用“先抽象再具体”的方法学习和理解复杂事物。 而我本人属前者。 这辈子不看内存地址和内存值;只画链表、指针示意图,画堆栈示意图,画各种示意图,甚至自己没画过而只看过书上的图……能从本质上理解指针、理解函数参数传递吗?本人深表怀疑! 这辈子不种麦不收麦不将麦粒拿去磨面;只吃馒头、吃面条、吃面包、……甚至从没看过别人怎么蒸馒头,压面条,烤面包,……能从本质上理解面粉、理解面食吗?本人深表怀疑!! 提醒: “学习用汇编语言写程序” 和 “VC调试(TC或BC用TD调试)时按Alt+8、Alt+6和Alt+5,打开汇编窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应内存和寄存器变化,这样过一遍不就啥都明白了吗。 (Linux或Unix下可以在用GDB调试时,看每句C对应的汇编并单步执行观察相应内存和寄存器变化。) 想要从本质上理解C指针,必须学习C和汇编的对应关系。” 不是一回事! 不要迷信书、考题、老师、回帖; 要迷信CPU、编译器、调试器、运行结果。 并请结合“盲人摸太阳”和“驾船出海时一定只带一个指南针。”加以理解。 任何理论、权威、传说、真理、标准、解释、想象、知识……都比不上摆在眼前的事实! 有人说一套做一套,你相信他说的还是相信他做的? 其实严格来说这个世界上古往今来所有人都是说一套做一套,不是吗? 不要写连自己也预测不了结果的代码! 电脑内存只是一个一维二进制字节数组及其对应的二进制地址; 人脑才将电脑内存中的这个一维二进制字节数组及其对应的二进制地址的某些部分看成是函数、函数参数、堆、栈、数组、指针、数组指针、指针数组、数组的数组、指针的指针、二维数组、……
lin5161678 2013-01-05
  • 打赏
  • 举报
回复
申请足够大的内存 然后用特定的数组指针指向他 就有这样的效果了 比如 申请 int[5][8] 这样的动态二维数组 int (*pArr)[8] = (int(*)[8]) malloc(sizeof(int[5][8])); 然后 pArr 就可以当成一个二维数组int[5][8]使用了 pArr[3][4]=100;
MoreWindows 2013-01-05
  • 打赏
  • 举报
回复
引用 2 楼 lile1234_show 的回复:
我并不是为了分来的, 只是来看看代码的。新手们应该好好看看。 不错,挺详细。
呵呵,谢谢你的赏识。 我也顺便散点分,方便大家在论坛上提问~
lee_鹿游原 2013-01-05
  • 打赏
  • 举报
回复
我并不是为了分来的, 只是来看看代码的。新手们应该好好看看。 不错,挺详细。
图灵狗 2013-01-05
  • 打赏
  • 举报
回复

33,321

社区成员

发帖
与我相关
我的任务
社区描述
C/C++ 新手乐园
社区管理员
  • 新手乐园社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧