C语言中2d数组声明的模糊性



我以下列格式定义了数组,但显然程序只在大小写b中工作正常。

案例一:

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]));

我在案例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作为输出。

问题可能在代码的其他地方。

CASE A;

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

M1[0][0]M[m-1][d-1]是获取int类型值的有效访问,否则将导致未定义的行为。

案例B:

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

在情况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<m; i++)
{
    M1[i] = (int *)malloc(d * sizeof(int));
} 

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

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

B的情况下,你声明是一个指向int的一维数组的指针,你为它分配了足够的空间来容纳你的二维数组。因为在C语言中不存在多维数组的概念,而是数组的数组的数组的数组的一个基本原理....等(随意),声明:

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

作为指向可修改数组的指针,并为[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]=%dn", i, j, (*M1)[i][j]);    //Check back data...
    }

现在看看如果你在这三种情况下超出边界会发生什么。在case A中,如果您超出了第一个下标的范围,您将收集一个错误的指针,这将立即导致内存故障,在case B中,只有当您超出进程可寻址内存时才会出现内存故障,正确的解决方案也是如此。这应该回答了你的问题

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

将形参声明为"array of type"时,应调整为"指向类型的限定指针",类型限定符(如果有的话)在其中在数组类型派生的[和]中指定的。如果关键字static也出现在数组类型的[和]中派生,然后为每次调用函数,值的对应的实际实参应提供对第一个的访问元素指定的元素数至少相同的数组元素大小的表达式。

它简单地声明数组将只通过引用传递(由编译器自动调整),而不是通过值传递(这对于结构体是合法的)。不要求在函数调用中提供任何限定的指针而不是数组,否则程序将崩溃()。参数声明为"类型的数组"应调整为"限定类型指针" -意味着将由编译器调整,而不是由您)。char *[]char **的情况是由于上面报道的原因,而不是因为交换它们是合法的!

相关内容

  • 没有找到相关文章

最新更新