C中二维数组声明中的歧义

我已经用以下格式定义了数组,但显然该程序仅在CASE B中正常工作。

案例A:

int **M1; M1 = (int **)malloc(m * sizeof(int *)); for (int i=0; i<m; i++){ M1[i] = (int *)malloc(d * sizeof(int)); } 

案例B:

 int (*M1)[m] = malloc(sizeof(int[m][d])); 

我在CASE A中遇到了分段错误。可能是什么原因?

以下代码在gcc编译器中编译时没有错误或警告,

  int **M1,i; M1 = (int **)malloc(5 * sizeof(int *)); for (i=0;i<5;i++){ M1[i] = (int *)malloc(2 * sizeof(int)); } M1[0][0]=111; M1[0][1]=222; printf("\n%d %d",M1[0][0],M1[0][1]); 

给出111 222作为输出。

问题可能是代码中的其他位置。

案例A;

M1是指向指针的指针。 您正在为int类型的2D数组分配内存,您可以在数组中存储m*d值。

M1[0][0]M[m-1][d-1]是此数组的有效访问权限,以获取int类型的值,除此之外的任何内容都将导致未定义的行为。

案例B:

M1是一个指向int类型数组的指针,它有m元素。

在案例A中,分段错误最有可能是因为数组超出绑定访问

指针和数组是不同的东西。 指向类型的指针包含分配声明类型变量的地址。

类型数组是一个内存空间,其中声明类型的变量是连续存储的

现在你宣布了。 在C声明中没有歧义(即使不是那么明显)。 如果您已声明A

 int **M1; 

这不是二维数组,甚至不是一维数组。 你在声明一个变量, M! ,作为指向int的另一个指针的指针。 在平面中,字M1将保持另一个变量的地址,该变量又保存存储器中存储整数的地址。 现在执行:

 M1 = (int **)malloc(m * sizeof(int *)); 

您可以为M1分配一个内存区域的地址, 该区域可以存储多达m个连续的整数指针 ,一个M1指向的内存访问,以及连续的位置充当数组访问 (但不是)。 这或多或少等同于静态声明:

 int *[m]; //an array of pointers to int 

然后为这个伪数组的每个元素分配一个d个连续整数的内存存储:

 for (int i=0; i 

你现在有空间存储d个连续的整数,这些整数从M1[i]为i = 0-> d-1保存的地址开始。

当您尝试使用下标访问值时会发生什么: M1[a][b] ? 编译器检索M1指向的地址并使用第一个下标( a ),检索指针数组的位置中的地址所指向的内容。 这指向我们分配用于保存d个连续int的子空间的第一个整数。 这确实是int的单维数组。 将第二个下标应用于它,编译器最终检索所需的整数。 Cose到二维数组寻址,但没有香蕉! 它不是int的二维数组。 🙂

B情况下,您声明指向int的单维数组的指针 ,您要为其分配足够的空间来容纳您的二维数组。 因为在C中不存在多维数组的概念,而是数组数组的基本原理....等等(ad-libitum),声明:

 int (*M1)[m] = malloc(sizeof(int[m][d])); 

作为指向modimensiional数组的指针,其中为[m][d]元素数组分配了空间。

但当然两种解决方案都是错误的!

您正在使用副作用来获得您想要的东西,但使用您声明需要的代理:int的二维数组。

正确的解决方案是定义一个指向二维数组整数的指针,其中只需要第一个下标:

 int (*M1)[][m]; //declare a pointer to a real array M1 = malloc(m * d * sizeof(int)); //Allocate room for bidimensional array for (int i=0; i 

现在看看如果你在3个案例中走出界限会发生什么。 在情况A中,如果你超出了第一个下标, 你将收集一个错误的指针,这将立即引起内存故障 ,如果B你只有在你没有进程可寻址内存时才会出现内存故障,同样的正确解决方案 这应该回答你的问题

最后,因为我们谈论的是关于数组和指针的误解,所以不要误解标准ISO 9899:2011§6.7.6.3/ 7,它说:

将参数声明为''类型数组''应调整为''限定指向类型'',其中类型限定符(如果有)是在数组类型派生的[和]中指定的那些。 如果关键字static也出现在数组类型派生的[和]中,那么对于每次对函数的调用,相应的实际参数的值应该提供对数组的第一个元素的访问,其中元素的数量至少与指定的元素一样多。按大小表达。

它只是声明数组将仅通过引用传递(由编译器自动调整),而不是通过值传递(对于结构即合法) 。 在函数调用中不要求您提供任何限定指针而不是数组,否则程序将崩溃( 参数声明为''数组''应调整为''限定指向类型'' - 表示将由编译器调整,而不是由您调整 。 作为char *[]char **适用于上述原因,而不是因为交换它们是合法的!