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

时间:2022-05-01 21:40:12

I have defined array in the following formats, but apparently the program works fine only in CASE B.

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

CASE A:

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

CASE B:

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

I am getting a segmentation error in CASE A. What could be the reason?

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

3 个解决方案

#1


3  

The following code compiles without error or warning in gcc compiler,

以下代码在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]);

Gives 111 222 as output.

给出111 222作为输出。

The problem might be some where else in your code.

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

#2


1  

CASE A;

M1 is a pointer to a pointer. You are allocating memory for a 2D array of type int and you can store m*d values in the array.

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

M1[0][0] to M[m-1][d-1] are valid access for this array in order to get value of type int and anything other than this will lead to undefined behavior.

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

Case B:

M1 is a pointer to an array of type int which has m elements.

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

In Case A, the segmentation fault most likely looks like is because of array out of bound access

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

#3


1  

A pointer and an array are different things. A pointer to a type holds the address where a variable of declared type is allocated.

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

An array of a type is a memory space where variables of the declared type are contiguously stored.

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

Now you're declaration. There is no ambiguity (even if is not so evident) in C declarations. In case A you have declared:

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

int **M1;

This is not a 2d array, not even a monodimensional one. You are declaring a variable, M!, as a pointer to another pointer to an int. In plane word M1 will hold the address of another variable that in turn holds the address of where in memory is stored an integer. Now executing:

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

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

You assign to M1 the address of a memory area that can store up to m contiguous integer pointers, an access to memory pointed by M1, and successive locations acts as an array access (but is not). This is more or less equivalent to the static declaration:

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

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

Then assigning each element of this pseudo-array a memory storage for d contiguous integers:

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

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

you now have room to store d consecutive integers that begins at addresses saved in M1[i] for i=0->d-1.

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

What happens when you try to access a value using subscripts: M1[a][b]? The compiler retrieve the address pointed by M1 and using the first subscript (a), retrieves the contents pointed by the address in the atht position of the pointers array. This points to the first integer of the subspace that we have allocated to hold d consecutive int's. This is indeed a monodimensional array of int. Applying the second subscript to it the compiler finally retrieve the required integer. Cose to a bidimensional array addressing, but no banana! It isn't a bidimensional array of int's. :-)

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

In case B you are declaring a pointer to a monodimensional array of int, to which you are assigning enough space to hold your bidimensional array. Because in C doesn't exist the concept of multidimensional array, but a basic principle of array of array of array.... etc. (ad-libitum), the declaration:

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

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

as a pointer to modimensiional array with space allocated for an array of [m][d] elements made the trick.

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

But of course both the solutions are wrong!

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

You are using side effects to obtain what you want, but using surrogates of what you declared to need: a bidimensional array of int's.

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

The correct solution is to define a pointer to a bidimensional array of integers where only the first subscript is required:

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

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...
    }

Now have a look to what happens if you go out bounds in the 3 cases. In case A if you get out of bounds with the first subscript you'll collect a wrong pointer that will cause immediately a memory fault, in case B you will have memory fault only if you go out of process addressable memory, the same for the correct solution. This should answer to your question.

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

Last, because we are talking of misunderstandigs about arrays and pointers, don't misinterpret the standard ISO 9899:2011§6.7.6.3/7 that says:

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

A declaration of a parameter as ‘‘array of type’’ shall be adjusted to ‘‘qualified pointer to type’’, where the type qualifiers (if any) are those specified within the [ and ] of the array type derivation. If the keyword static also appears within the [ and ] of the array type derivation, then for each call to the function, the value of the corresponding actual argument shall provide access to the first element of an array with at least as many elements as specified by the size expression.

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

It simply states that arrays will be passed only by reference (automatically adjusted by compiler), never by value (that is legal for structs ie). You are not requested to supply any qualified pointer instead of an array in function call or the program will crash (A declaration of a parameter as ‘‘array of type’’ shall be adjusted to ‘‘qualified pointer to type’’ - means that will be adjusted by compiler, not by you). Cases as char *[] and char ** works for the reason reported above, not because it is legal to exchange them!

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

#1


3  

The following code compiles without error or warning in gcc compiler,

以下代码在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]);

Gives 111 222 as output.

给出111 222作为输出。

The problem might be some where else in your code.

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

#2


1  

CASE A;

M1 is a pointer to a pointer. You are allocating memory for a 2D array of type int and you can store m*d values in the array.

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

M1[0][0] to M[m-1][d-1] are valid access for this array in order to get value of type int and anything other than this will lead to undefined behavior.

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

Case B:

M1 is a pointer to an array of type int which has m elements.

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

In Case A, the segmentation fault most likely looks like is because of array out of bound access

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

#3


1  

A pointer and an array are different things. A pointer to a type holds the address where a variable of declared type is allocated.

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

An array of a type is a memory space where variables of the declared type are contiguously stored.

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

Now you're declaration. There is no ambiguity (even if is not so evident) in C declarations. In case A you have declared:

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

int **M1;

This is not a 2d array, not even a monodimensional one. You are declaring a variable, M!, as a pointer to another pointer to an int. In plane word M1 will hold the address of another variable that in turn holds the address of where in memory is stored an integer. Now executing:

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

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

You assign to M1 the address of a memory area that can store up to m contiguous integer pointers, an access to memory pointed by M1, and successive locations acts as an array access (but is not). This is more or less equivalent to the static declaration:

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

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

Then assigning each element of this pseudo-array a memory storage for d contiguous integers:

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

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

you now have room to store d consecutive integers that begins at addresses saved in M1[i] for i=0->d-1.

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

What happens when you try to access a value using subscripts: M1[a][b]? The compiler retrieve the address pointed by M1 and using the first subscript (a), retrieves the contents pointed by the address in the atht position of the pointers array. This points to the first integer of the subspace that we have allocated to hold d consecutive int's. This is indeed a monodimensional array of int. Applying the second subscript to it the compiler finally retrieve the required integer. Cose to a bidimensional array addressing, but no banana! It isn't a bidimensional array of int's. :-)

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

In case B you are declaring a pointer to a monodimensional array of int, to which you are assigning enough space to hold your bidimensional array. Because in C doesn't exist the concept of multidimensional array, but a basic principle of array of array of array.... etc. (ad-libitum), the declaration:

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

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

as a pointer to modimensiional array with space allocated for an array of [m][d] elements made the trick.

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

But of course both the solutions are wrong!

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

You are using side effects to obtain what you want, but using surrogates of what you declared to need: a bidimensional array of int's.

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

The correct solution is to define a pointer to a bidimensional array of integers where only the first subscript is required:

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

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...
    }

Now have a look to what happens if you go out bounds in the 3 cases. In case A if you get out of bounds with the first subscript you'll collect a wrong pointer that will cause immediately a memory fault, in case B you will have memory fault only if you go out of process addressable memory, the same for the correct solution. This should answer to your question.

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

Last, because we are talking of misunderstandigs about arrays and pointers, don't misinterpret the standard ISO 9899:2011§6.7.6.3/7 that says:

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

A declaration of a parameter as ‘‘array of type’’ shall be adjusted to ‘‘qualified pointer to type’’, where the type qualifiers (if any) are those specified within the [ and ] of the array type derivation. If the keyword static also appears within the [ and ] of the array type derivation, then for each call to the function, the value of the corresponding actual argument shall provide access to the first element of an array with at least as many elements as specified by the size expression.

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

It simply states that arrays will be passed only by reference (automatically adjusted by compiler), never by value (that is legal for structs ie). You are not requested to supply any qualified pointer instead of an array in function call or the program will crash (A declaration of a parameter as ‘‘array of type’’ shall be adjusted to ‘‘qualified pointer to type’’ - means that will be adjusted by compiler, not by you). Cases as char *[] and char ** works for the reason reported above, not because it is legal to exchange them!

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