【程序分析】指针与二维数组的访问

访问二维数组的指针问题

一级/二级指针访问二维数组看似是个简单的问题,但在实际编程中发现还是有不少值得学习的细节问题,本文总结如下

指针解引用过程

指针解引用过程

声明一个指针类型使用该指针访问数据的解引用过程如上图所示,关键点如下

  • 要理解指针P本身的值被解释为数据的起始内存地址
  • 指针P指向的数据类型TYPE是什么(TYPE当然还是一个指针类型,从而有了后文的二级指针使用注意事项)
  • *P返回的是从起始内存地址开始sizeof(TYPE)个字节数据
  • 由于指针P指向的数据类型TYPE,所以P++或者P+1不是按一个字节做偏移,而是按sizeof(TYPE)个字节做偏移,这里是最容易引起错误的地方

二维数组的访问

理解了指针的解引用过程,就能理解二维数组访问过程中几个细节的问题了

问题1:指针赋值二维数组

int main(void)
{
        float A[K][N]={ {1,2,3,4}, {5,6,7,8}, {9,10,11,12} };
        float* p1 = A;//编译出错
        float** p2 = A;//编译出错

        return 0;
}

上面的代码在编译阶段就会发生如下错误,所以证明二维数组的类型与一级/二级指针不是同一种类型可以直接赋值

error: cannot convert ' float (*)[4] ' to ' float* ' 

error: cannot convert ' float (*)[4] ' to ' float** '

 

问题2:指针访问二维数组

int main(void)
{
        float A[K][N]={ {1,2,3,4}, {5,6,7,8}, {9,10,11,12} };
        float* p1 = (float*)A;
        float** p2 = (float**)A;
        std::cout << " address of A: " << A << " sizeof(A): " << sizeof(A) << std::endl;
        std::cout << " address of p1: " << p1 << " addressof (p1+1): " << (p1+1) << " sizeof(p1): " << sizeof(p1) << " sizeof(*p1): " << sizeof(*p1) << std::endl;
    std::cout << " address of p2: " << p2 << " addressof (p2+1): " << (p2+1) << " sizeof(p2): " << sizeof(p2) << " sizeof(*p2): " << sizeof(*p2) << std::endl;
        return 0;
}

强制类型转换可以解决二维数组赋值给一级/二级指针的问题

运行上面的程序获得的结果如下:

address of A: 0x7ff4c8a368  sizeof(A): 48

address of p1: 0x7ff4c8a368 address of (p1+1): 0x7ff4c8a36c  sizeof(p1): 8  sizeof(*p1): 4

address of p2: 0x7ff4c8a368 address of (p2+1): 0x7ff4c8a370  sizeof(p2): 8  sizeof(*p2): 8

上述结果的差异性用下图解释:

程序运行结果详解

有了上面的基础,就可以进一步解释为什么不能用二级指针访问二维数组了

int main(void)
{
        float A[K][N]={ {1,2,3,4}, {5,6,7,8}, {9,10,11,12} };
        float* p1 = (float*)A;
        float** p2 = (float**)A;
        std::cout << " p2[0][0]: " << p2[0][0] << std::endl; //发生segmentation fault crash
        return 0;
}

运行上面的程序试图通过二级指针p2[0][0]去访问二维数组A[0][0]元素,结果程序直接发生segmentation fault crash,原因用下图解释

二级指针访问二维数组crash的原因分析

所以通过二级指针去遍历二维数组是不正确的,但是却可以通过一级指针去访问二维数组

int main(void)
{
    float A[K][N]={ {1,2,3,4}, {5,6,7,8}, {9,10,11,12} };
    float* p1 = (float*)A;
    float** p2 = (float**)A;
    int row = K;
    int col = N;
    for(int i=0;i<row;i++)
    {
        std::cout << std::endl;
        std::cout << " [ ";
        for (int j=0; j<col; j++) {
         std::cout << p1[i*col+j] << " ";
        }
        std::cout << " ] ";
    }
    std::cout << std::endl;
        return 0;
}

 程序运行后输出的结果=\begin{bmatrix} 1& 2& 3& 4\\ 5& 6& 7& 8\\ 9& 10& 11& 12 \end{bmatrix}

访问过程如下图所示

一级指针访问二维数组的过程分析

 

问题3:二维数组传参

基于对问题1和问题2的分析可以知道,将二维数组作为参数传递的时候,匹配的形参不能使用二级指针,但是可以使用一级指针,下面的表格示例了几种组合

 问题4:指针数组与数组指针

int main(void)
{
        float A[K][N]={ {1,2,3,4}, {5,6,7,8}, {9,10,11,12} };
        float *p1[N];
        float (*p2)[N];
        std::cout << " address of A: " << A << " sizeof(A): " << sizeof(A) << std::endl;
        std::cout << " address of p1: " << p1 << " addressof (p1+1): " << (p1+1) << " sizeof(p1): " << sizeof(p1) << " sizeof(*p1): " << sizeof(*p1) << std::endl;
    std::cout << " address of p2: " << p2 << " addressof (p2+1): " << (p2+1) << " sizeof(p2): " << sizeof(p2) << " sizeof(*p2): " << sizeof(*p2) << std::endl;
        return 0;
}

运行上面的程序会得到类似如下的结果:

address of A: 0x7ff4c8a368  sizeof(A): 48

address of p1: 0x7ff4c8a348 address of (p1+1): 0x7ff4c8a350  sizeof(p1): 32  sizeof(*p1): 8

address of p2: 0x7ff4c8a360 address of (p2+1): 0x7ff4c8a370  sizeof(p2): 8  sizeof(*p2): 16

此处的float *p1[N] 是个指针数组,它与 float (*p2)[N]数组指针 的差异如下图所示

指针数组与数组指针的差异

 

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值