使用pointer to pointer访问二维数组触发Segmentation fault的原因与解决
为什么二级指针无法正确访问二维数组的非第一行元素?
这是个很典型的C语言类型不匹配问题,咱们一步一步拆解:
先看你的代码和运行结果:
代码:
#include <stdio.h> int myfunction(double **U) { printf("%f\n", U[0][0]); //正确打印U[0][0] printf("%f\n", U[0][1]); //正确打印U[0][1] printf("%f\n", U[1][0]); //触发"Segmentation fault (core dumped)" return 0; } int main(void){ double U[3][4] = {{10, 1, 0, 0}, {2, 6, 0, 0}, {4, 3, 5, 0}}; double **pU = U; myfunction(&pU); return 0; }
运行输出:
10.000000 1.000000 Segmentation fault (core dumped)
为什么只能访问第一行?
问题核心是二维数组和double **(二级指针)的内存模型完全不匹配:
- 你声明的
double U[3][4]是连续存储的二维数组,在内存里它是12个double值紧密排列的:10,1,0,0,2,6,0,0,4,3,5,0。它的实际类型是double[3][4],把它直接赋值给double **pU属于非法类型转换——如果开启了高等级编译警告,编译器会明确提示你这一点。 - 而
double **的本质是「指向指针的指针」,它期望的内存结构是:先有一个指针数组,数组里每个元素都是指向某一行double数组的指针,然后double **指向这个指针数组的首地址。
那前两个printf能正常工作纯属巧合:
- 你把
&pU传给myfunction(这里又多了一层指针,&pU是double ***类型,却被当成double **使用),函数里的U[0]实际取到的是pU的值,也就是原数组的首地址&U[0][0],所以U[0][0]和U[0][1]能正确访问数组前两个元素。 - 但访问
U[1][0]时,U[1]会尝试取U指针往后偏移一个double *大小的地址上的内容(通常是8字节),并把它当成指针——但这个地址里的内容其实是数组的第二个元素1.0,把浮点数1.0转换成指针地址显然是无效的,访问它自然触发段错误。
怎么修改才能正确访问所有元素?
有两种常用解决方案,根据你的需求选择:
方案1:使用指向数组的指针(最直接,适合固定列数的二维数组)
修改函数参数类型,让它接受一个指向4列double数组的指针,完美匹配原二维数组的类型:
#include <stdio.h> // 参数是指向4列double数组的指针 int myfunction(double (*U)[4]) { printf("%f\n", U[0][0]); printf("%f\n", U[0][1]); printf("%f\n", U[1][0]); // 现在可以正确打印2.000000 printf("%f\n", U[2][2]); // 也能访问第三行第三列的5.000000 return 0; } int main(void){ double U[3][4] = {{10, 1, 0, 0}, {2, 6, 0, 0}, {4, 3, 5, 0}}; // U会隐式转换成指向第一行数组的指针,直接传递即可 myfunction(U); return 0; }
方案2:用指针数组适配二级指针(适合动态行或列数不固定的场景)
如果你一定要用double **作为函数参数,需要先创建一个指针数组,把二维数组每一行的首地址存进去,再将这个指针数组传给函数:
#include <stdio.h> int myfunction(double **U) { printf("%f\n", U[0][0]); printf("%f\n", U[0][1]); printf("%f\n", U[1][0]); // 正确打印2.000000 return 0; } int main(void){ double U[3][4] = {{10, 1, 0, 0}, {2, 6, 0, 0}, {4, 3, 5, 0}}; // 创建指针数组,存储每一行的首地址 double *rows[] = {U[0], U[1], U[2]}; double **pU = rows; // 直接传递pU,它是标准的double**类型 myfunction(pU); return 0; }
总结
永远记住:C语言中二维数组≠二级指针,它们的内存模型完全不同。如果是固定大小的二维数组,优先用指向数组的指针传递;如果需要动态结构,再用二级指针配合指针数组实现。
内容的提问来源于stack exchange,提问作者A. A. Pedersen
相关产品推荐
相关产品推荐

