C++ 使用new分配二维数组的一些想法和建议

1 如何理解一个二维数组 int a[m][n]?

我觉得有两种很自然的想法。

  1. 由m个一维数组组成
  2. 二维数组其实就是一个一维数组。
    上面两种方式并不矛盾,展现了二维数组在内存中的组织形式。下面分析我们经常看到的两种写法。

2 由m个一维数组组成

我相信这个代码,大家都经常用,尤其在使用C语言(malloc/free)的时候。
下面简要的分析下流程。

  1. 分配:先创建数组名 array (分配多少行),在for循环中分配列。
  2. 释放:先释放每行数组,最后释放array

备注:这种反向操作的是很常见的,比如栈就符合,构造/析构。从逻辑上、物理上符合栈,可以找到很多例子。

	// 创建二维数组 array[2][3]
	int** array = new int*[2];         // 先分配行--2行
	for (size_t i = 0; i < 2; i++)
	    array[i] = new int[3];         // 后分配列--3列
	
	int i = 0;
	for (int r = 0; r < 2; r++)         // 访问
	    for (int c = 0; c < 3; c++)
	        array[r][c] = i++;
	
	// 释放 array[2][3]
	for (int i = 0; i < 2; ++i)        // 释放和分配是反着的,先释放每行数组 
	    if (array[i] != nullptr)
	        delete[] array[i];
	
	if (array == nullptr)              // 释放数组名--第一步分配的
	    delete[] array;                // 这里要用delete[]

为什么说由m个一维数组组成?
从感觉上就知道,每一次for循环创建一个一维数组,大部分情况下,这2个数组都不是挨着的

  1. 当然,数组内是相邻的
  2. 数组间不相邻
    在这里插入图片描述

3 二维数组其实就是一个一维数组

咋一看,这份代码比上面清晰,简洁很多,不需要用for分配数组,也不需要用for释放内存
当然,也有人会怀疑这代码释放内存有问题
其实,这份代码没有问题,此时你会有一个疑问,delete释放当个元素,delete[]释放一维数组
难道delete[]也可以释放多维数组,可以肯定的是教科书上从来没有这么讲,也没有有人敢这么说,这本就是错的。

但是,有一种情况下是对的,二维数组本就是连续的(内存),它本就是一维数组。同时对于三维数组,或者高维数组也是对的!!!
	   // 创建二维数组 array[2][3]
	   int (*array)[3] = new int[2][3];
	   if (array == nullptr)
	       return 0;
	   
	   int i = 0;                      
	   for (int r = 0; r < 2; r++)     // 访问
	       for (int c = 0; c < 3; c++)
	           array[r][c] = i++;
	
	   // 释放 array[2][3]
	   if (array != nullptr) // 能理解为什么delete[]可以用在二维数组上了把,本质是一个一维数组
	       delete[] array;

从感觉上就知道,一次性分配好,很可能是连续的(一维数组)。

  1. 当然,数组内是相邻的
  2. 数组间也是相邻的!!!
    在这里插入图片描述

3 如何选择这两种方式?

总结:这两种分配的最本质的区别,就是连续与否(各一维数组是否连续)
这里就引出一个问题,如果数组总数量很大,比如几W个,第二种就会多次寻找符合这样大的连续内存的空间,当然对于内存较小的单片机,微机这是很不现实,也很难实现。反之,使用第一种分配方式就把压力减少到1/m。

一些建议

  1. 对于第一种方式,适合在内存资源要求严格的地方,当然C语言malloc/free也是这一梯队。比如单片机等。这里给出一个概念,1M能存可以存 2 16 = 65536 2^{16}=65536 216=65536个int,刚好是windows栈爆。缺点:分配和释放需要for,繁杂
  2. 第二种方式,代码简洁,易懂,更加贴近人的思维。但是由于分配的数组是连续的,在找这个大内存块造成了压力。如果你的内存很大,当然可以忽略,如果你是C++选手,可以写的更加简单
    // 创建二维数组 array[2][3]
    auto array = new int[2][3];

    int i = 0;
    for (int r = 0; r < 2; r++)         // 访问
        for (int c = 0; c < 3; c++)
                array[r][c] = i++;

    // 释放 array[2][3]
    delete[] array;

当然,这种方式还可以适合三维数组,高维数组,优势就来了。

    // 创建三维数组 array[2][3][4]
    auto array = new int[2][3][4];		// (*array)[3][4]

    int i = 0;
    for (int r = 0; r < 2; r++)         // 访问
        for (int c = 0; c < 3; c++)
            for (int h = 0; h < 4; h++)
                array[r][c][h] = i++;

    delete[] array;

4 关于(*array)[2]

这就是典型的数组指针,它的本质是数组,而数组元素存的是指针。
其实数组指针非常常见,里面还可以存函数,也就是函数指针,比如array是function Name;当然有时候还省略名字(*)[2],看到把*用括号包起来绝对是函数指针,,,

这里分享如何记住数组指针和指针数组?

谁在前,本质就是谁
数组指针:数组在前,本质是数组,而数组元素存的是指针
指针数组:指针在前,本质是指针,指向的是数组
  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值