zoukankan      html  css  js  c++  java
  • 动态数组(一维二维)探秘

    因为做leetcode的一道算法题https://leetcode-cn.com/problems/regular-expression-matching/,需要用到二维数组,这里就详细介绍一下数组

    一维数组

        char * a = (char*)malloc(8 * sizeof(char));
        memset(a, 0, 8);
        for (int i = 0; i < 8; i++)
        {
            *(a + i) = 'a' + i;
        }
        for (int i = 0; i < 8; i++)
        {
            cout << *(a+i);
        }
        for (int i = 0; i < 8; i++)
        {
            cout << a[i];
        }
        free(a);

    输出内容abcdefghabcdefgh

    这样申请与char a[8]是一样的,不管是底层实现还是使用,都是一样的,这就是一个8个字节长度的数组。我们看内存,也是一段连续的数据。

    释放内存的时候,系统可以根据内存管理把这连续的8个字节的空间回收。

    二维数组

    我们先看一下正常的用法

        char a[8][4] = { 0 };
        for (int i = 0; i < 8; i++)
        {
            for (int j = 0; j < 4; j++)
            {
                a[i][j] = 'a' + i;
            }
        }
        for (int i = 0; i < 8; i++)
        {
            for (int j = 0; j < 4; j++)
            {
                cout << a[i][j];
            }
        }

    输出结果 aaaabbbbccccddddeeeeffffgggghhhh

    我们看一下内存

    是一段连续的内存,数组a[n][m],按照m跳度,前m个是第一个n的序列,然后是第二个n的m个序列,实际上这是一个一维数组。

    看看我们第一种实现

            char** a = (char**)malloc(8 * sizeof(char*));
            for (int i = 0; i < 8; i++)
            {
                *(a + i) = (char*)malloc(4 * sizeof(char));
            }
            for (int i = 0; i < 8; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    a[i][j] = 'a' + i;
                }
            }
            for (int i = 0; i < 8; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    cout << a[i][j];
                }
            }

    a是一个数组,也就是a指向了申请的一块空间,空间是8个char*大小。然后为a的每一个数组元素,又指向了一块空间,空间是4个char大小。内存空间如图所示,a指向了一块空间,有8个元素,每个元素的空间用来保存了一个char*的数据,也就是char的指针,a算是一个指向指针的指针。a是一个指针,指向了一块数据,这块数据保存的是一个指针,指向了一块数据,这块数据保存的是一个4字节的char。

     我们看一下实际的内存地址

    这是char*数组a的数据,8个char*的空间,标蓝的是第一个a+0保存的数据,是一个指针地址,因为是32位的,所以是4个字节,翻译过来就是0X00557600

    我们看一下a的第一块数据指向的内容,是一块数据,保存了aaaa,四个a。

    多维数组可以这样理解,一维数组a指向了一块数据,保存的是字符;二维数组指向了一块数据,保存的相当于是一个一维数组指针的列表,每个二维数组元素相当于一个一维数组;三维数组就是保存了一个二维数组指针的列表,以此类推,一层层解析。

    上面这种二维数组是如何访问数据呢,先通过a+i访问到a的元素指针,然后通过*(a+i)访问到数据,这个数据又是个数组,通过*(a+i)+j访问到元素指针,再通过*(*(a+i)+j)访问到数据,最后提醒,别忘了释放空间。

            for (int i = 0; i < 8; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    cout << *(*(a + i) + j);
                }
            }
            for (int i = 0; i < 8; i++)
            {
                free(*(a + i));
            }
            free(a);

    二维数组的第二种实现

    我们看到,如果通过char a[8][4]申请一个二维数组,实际上空间是连续的,我们能不能模拟呢,这样整块申请内存,效率更高。代码如下

            char* a = (char*)malloc(8 * 4 * sizeof(char));
    
            for (int i = 0; i < 8; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    *(a + i * 4 + j) = 'a' + i;
                }
            }
            for (int i = 0; i < 8; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    cout << *(a + i * 4 + j);
                }
            }
            free(a);

    申请了一个一维数组,大小是a[n][m]中n*m个数组元素个数。访问呢,就模仿二维数组的访问,m个元素是一组。内存如下

    这里有一个小小的一问,char a[8][4]这种可以通过 a[1][2]访问,但是这种方式不行。char[8][4]系统知道是一个二维数组,保留了它的信息,知道如何跳,而char* a = (char*)malloc(8 * 4 * sizeof(char));系统认为是一维数组,按照二维的那种方式跳,就会运行错误。

    插曲

    除了malloc,也可以用new,更方便一些

    char * a = new char[8]();

    减少了初始化的步骤,这里就是创建一块空间,大小是8,单位是char,调用()初始化。这里()的意思就是调用默认构造函数等于new char[8]{}

    二维数组的第三种方法

    上面的方法空间在一起了,但是无法用二维数组的方式访问 ,很别扭,那么有没有动态二维数组呢,上面插曲的new为我们提供了方法。

            auto a = new char[8][4]();
            for (int i = 0; i < 8; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    a[i][j] = 'a' + i;
                }
            }
            for (int i = 0; i < 8; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    cout << a[i][j];
                }
            }
            delete[](a);

    auto a = new char[8][4]()到底是什么

    这种方法的实质是什么呢?实际上就是第二种方法,只不过告诉了系统,这是一个二维数组。那么二维数组的指针怎么定义呢?看下面的示例

            char(* a)[4] = (char(*)[4])malloc(8 * 4 * sizeof(char));
    
            for (int i = 0; i < 8; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    a[i][j] = 'a' + i;
                }
            }
            for (int i = 0; i < 8; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    cout << a[i][j];
                }
            }
            free(a);

     进一步了解详情请看数组、函数与指针

  • 相关阅读:
    pytest05-参数化
    pytest04-conftest配置文件
    pytest03-fixture
    pytest02-setup和teardown
    SimpleDateFormat 是线程不安全的类,一般不要定义为 static 变量,如果定义为 static ,必须加锁,或者使用 DateUtils 工具类
    线程池不使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式
    线程资源最好通过线程池提供
    获取单例对象需要保证线程安全,其中的方法也要保证线程安全
    高度注意 Map 类集合 K / V 能不能存储 null 值的情况,如下表格
    使用 entrySet 遍历 Map 类集合 KV ,而不是 keySet 方式进行遍历的好处
  • 原文地址:https://www.cnblogs.com/studywithallofyou/p/12091928.html
Copyright © 2011-2022 走看看