二维数组和一维数组的数据分布和存取

二维数组和一维数组的数据分布和存取

标签:c/c++


  • 二维数组在存储分布上和一维数组是一样的,但是存取的写法却是有很大差异的

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int main()
    {
        int i, j;
        char mark[64][64] = {0};
        char a = 1;
    
        for (i = 0; i < 64; i++){
            for (j = 0; j < 64; j++){
                memcpy(mark + i * 64 + j, &a, sizeof(char));
            }
        }
    
        return 0;
    }
    

      上述代码会越界(如果换作一维数组就不会发生越界),原因是mark是二维数组的首地址,所以对mark进行+1操作,事实上得到的结果表示的是mark的下一维[64]数组的首地址,即一次加上的是整个第二维数组的大小。

  • 如何像操作一维数组一样操作二维数组
      在实际的项目中,可能会经常涉及到将二维数组改成一维数组的情况,也会经常执行类似于从数组中取值的操作。

    • 如果操作是从数组第一个元素(0, 0)开始
        我们可以用数组名初始化指针,之后用指针进行相关操作,没有问题(见下面例子,这种方法其实也不好操作)。此时如果是选用数组名进行操作,需要特别注意不能出现上述情况(在二维数组名上进行加法运算不等同于“遍历”数组元素)。

    • 如果是从数组中间进行取值
        我们可以用指针指向该元素,之后用指针进行相关的操作。但是,前提是一定要找准该元素的地址。举两个例子(例子中将二维数组的第一维第二维分别成作“行”和“列”;mark_[64*64]是mark对应的转换后的一维数组):
      (1)从第二列首地址开始
      char *p = mark[1];
      对应的将二维数组转换为一维数组的写法:
      &mark_[1*64] <=> mark[1]
      (2)从第二列的第三个元素开始
      char *p = &mark[1][2];
      对应的将二维数组转换一维数组的写法:
      &mark_[1*64+2] <=> &mark[1][2]

  • 二维数组名初始化指针问题

    // 略去头文件
    
    int main()
    {
        int a[3][3] = {
            {1, 2, 3},
            {3, 4, 5},
            {6, 7, 8}
        };
    
        int *p1 = a;         //[1]
        printf("*p:%d %d\n", *p1 , *(p1+1));
    
        int **p2 = a;        //[2]
        printf("**p:%d %d\n", *p2, *(p2+1));
    
        int(*p3)[3] = a;     //[3]
        printf("(*p)[]:%d %d\n", (*p3)[0], (*p3)[1]);
        printf("(*p)[]:%d %d\n", *(*p3), *(*(p3)+1));
    
        int sd = 0;
    
        return 0;
    }
    • 注释[1]
        g++或gcc编译器会直接报错: test.cpp:18:14: error: cannot convert ‘int (*)[3]’ to ‘int*’ in initialization int *p1 = a; ;vs下可以编译通过,且通过p1指针(+1或++),我们可以对二维数组进行遍历操作,可见vs的编译器在这种情况下会对数组名a进行类型转换,即将 int (*)[3] 转换为 int *不要使用这种写法,因为会出现不兼容的问题。

    • 注释[2]
        g++或gcc编译器会直接报错: test.cpp:18:14: error: cannot convert ‘int (*)[3]’ to ‘int**’ in initialization int **p2 = a; ;vs下可以编译通过,且通过p2二维指针(+1或++),我们可以对二维数组进行遍历操作,可见vs的编译器在这种情况下也会对数组名a进行类型转换,即将 int (*)[3] 转换为 int **
        注意这里是【int 】,因为是二维数组的缘故,此时会发现*p2依旧是一个指针,只不过这个指针存储的“地址”是a[0][0]对应的值;那么相应的,p2是指针的指针,存储的是*p2的地址;但是,**p存储的就是未知的值了,因为a[0][0]作为地址再去取地址的值,会发生意想不到的问题,内存越界!**不要使用这种写法,因为会出现不兼容的问题。

    • 注释[3]
        正常的写法,不会出现强制类型转换,但是你会发现这种方法和数组操作没有太大区别,因为类型的关系,*p表示的是只是一个地址,如果需要取值,则外层需要再有一个取地址符 * 。正常的写法,推荐。

  • 小结
      代码中使用的应该是一维数组到指针的转换,或者是多维指针通过calloc/malloc获取空间之后的相互转换(指向)。不应该存在上述过程中的多维数组到指针之间的转换。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值