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

时间:2015-08-13 08:07:41

标签: c arrays pointers malloc

我已经用以下格式定义了数组,但显然该程序仅在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中遇到了分段错误。可能是什么原因?

3 个答案:

答案 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 **的案例适用于上述原因,而不是因为交换它们是合法的!