访问二维数组的指针问题
一级/二级指针访问二维数组看似是个简单的问题,但在实际编程中发现还是有不少值得学习的细节问题,本文总结如下
指针解引用过程
![](https://img-blog.csdnimg.cn/20191114095945855.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0hhb0JCTnVhbk1N,size_16,color_FFFFFF,t_70)
声明一个指针类型使用该指针访问数据的解引用过程如上图所示,关键点如下
- 要理解指针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
上述结果的差异性用下图解释:
![](https://img-blog.csdnimg.cn/20191114105102650.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0hhb0JCTnVhbk1N,size_16,color_FFFFFF,t_70)
有了上面的基础,就可以进一步解释为什么不能用二级指针访问二维数组了
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,原因用下图解释
![](https://img-blog.csdnimg.cn/20191114110800426.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0hhb0JCTnVhbk1N,size_16,color_FFFFFF,t_70)
所以通过二级指针去遍历二维数组是不正确的,但是却可以通过一级指针去访问二维数组
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;
}
程序运行后输出的结果=
访问过程如下图所示
![](https://img-blog.csdnimg.cn/20191114112522462.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0hhb0JCTnVhbk1N,size_16,color_FFFFFF,t_70)
问题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]数组指针 的差异如下图所示
![](https://img-blog.csdnimg.cn/20191114134955446.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0hhb0JCTnVhbk1N,size_16,color_FFFFFF,t_70)