我已经用以下格式定义了数组,但显然该程序仅在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中遇到了分段错误。可能是什么原因?
答案 0 :(得分:3)
以下代码在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
作为输出。
问题可能是代码中的其他位置。
答案 1 :(得分:1)
案例A;
M1
是指向指针的指针。
您正在为int类型的2D数组分配内存,您可以在数组中存储m*d
值。
M1[0][0]
到M[m-1][d-1]
是此数组的有效访问权限,以获取类型int
的值,除此之外的任何内容都将导致未定义的行为。
案例B:
M1是一个指向int类型数组的指针,该数组包含m
个元素。
在案例A中,分段错误很可能是因为数组超出限制访问
答案 2 :(得分:1)
指针和数组是不同的东西。 指向类型的指针包含分配声明类型变量的地址。
类型数组是一个内存空间,其中声明类型的变量连续存储。
现在你宣布了。 C声明中没有歧义(即使不是那么明显)。 如果您已声明 A :
int **M1;
这不是二维数组,甚至不是一维数组。您声明一个变量M!
,作为指向另一个int指针的指针。在平面中,单词M1
将保存另一个变量的地址,该变量又保存存储器中存储整数的地址。
现在执行:
M1 = (int **)malloc(m * sizeof(int *));
您可以为M1
分配一个可以存储多达m个连续整数指针的内存区域的地址,M1
指向的内存访问以及连续位置< strong>充当数组访问(但不是)。这或多或少等同于静态声明:
int *[m]; //an array of pointers to int
然后为此伪数组的每个元素分配d
个连续整数的内存存储:
for (int i=0; i<m; i++)
{
M1[i] = (int *)malloc(d * sizeof(int));
}
您现在可以存储d
个连续整数的空间,这些整数从M1[i]
中为i = 0-&gt; 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<m; i++)
for (int j=0; j<d; j++)
{
(*M1)[i][j] = (i*100)+j; //Fill elements with something using bidimensional subscripting
}
for (int i=0; i<m; i++)
for (int j=0; j<d; j++)
{
printf("[%d][%d]=%d\n", i, j, (*M1)[i][j]); //Check back data...
}
现在看看如果你在3个案例中走出界限会发生什么。 如果 A ,如果超出第一个下标,你将收集一个错误的指针,这将立即导致内存错误,以防 B 只有当你离开进程可寻址内存时才会出现内存故障,正确的解决方案也是如此。 这应该回答您的问题。
最后,因为我们谈论的是关于数组和指针的误解,所以不要误解标准的ISO 9899:2011§6.7.6.3/ 7:
参数声明为''数组类型''应调整为 ''类型''的限定指针,类型限定符(如果有的话) 在数组类型派生的[和]内指定的那些。如果 关键字static也出现在数组类型的[和]中 派生,然后对每个函数的调用,值的 相应的实际参数应提供对第一个的访问权限 数组的元素,其元素至少与指定的元素一样多 大小表达。
它只是声明数组将仅通过引用传递(由编译器自动调整),永远不会通过值传递(对结构来说是合法的,即)。在函数调用中不要求您提供任何限定指针而不是数组,否则程序将崩溃(参数声明为“'类型数组''应调整为
''指向类型'的指针' - 表示将由编译器调整,而不是由你调整)。 char *[]
和char **
的案例适用于上述原因,而不是因为交换它们是合法的!