简体   繁体   English

在 C 中,当使用 malloc 创建二维数组时,为什么在传递给函数时不需要指定二维数组大小?

[英]In C why do I NOT need to specify 2D array size when passing into function when the 2D array is created with malloc?

I'm pretty new with C and just confused with what's really happening when I'm passing 2D arrays allocated in HEAP memory into a function.我对C很陌生,只是对当我将分配在HEAP内存中的二维数组传递给函数时真正发生的事情感到困惑。 I've written code which has three functions, A, B, C which demonstrates my question.我编写了具有三个函数 A、B、C 的代码,它演示了我的问题。

Essentially, when I create a 2d array in stack space in function-A, I am able to pass that 2d array pointer to a function-B which requires the parameter (int size, int (*arr)[size]) and that works fine.本质上,当我在函数 A 的堆栈空间中创建一个二维数组时,我能够将该二维数组指针传递给一个需要参数(int size, int (*arr)[size])的函数 B,它可以工作美好的。 My understanding is the 'int size' variable is required to let arr pointer now how much space it should jump each increment我的理解是'int size'变量需要让arr指针现在每个增量应该跳跃多少空间

However, when I create a 2d array in HEAP space in function-A, passing it to function-B appears to lose the location of the data (see code).但是,当我在函数 A 的 HEAP 空间中创建二维数组时,将其传递给函数 B 似乎丢失了数据的位置(请参阅代码)。 However if I pass this HEAP space 2d array to function-C which has the parameter (int **arr) , it works fine.但是,如果我将此 HEAP 空间二维数组传递给具有参数(int **arr) function-C,则它可以正常工作。

It would be great if someone could try to explain why I don't need to specify size when passing the HEAP space 2d array into function-C.如果有人可以尝试解释为什么在将 HEAP 空间二维数组传递给函数 C 时我不需要指定大小,那就太好了。 Also, when I pass the 2d array created in STACK space to function-C, it crashes, why is that?另外,当我将在STACK空间中创建的二维数组传递给函数 C 时,它崩溃了,这是为什么?

Here is sample code showcasing my question ( Output is this ):这是展示我的问题的示例代码(输出是这个):

#include <stdio.h>
#include <stdlib.h>

void function_A(int num)
{
    // allocating HEAP space for 2D array
    int **arrHEAP = (int **)malloc(2*sizeof(int*)); 
    arrHEAP[0] = (int *)malloc(5*sizeof(int));
    arrHEAP[1] = (int *)malloc(5*sizeof(int));
    for(int i=0;i<2;i++) // initialising
        for(int j=0;j<5;j++)
            arrHEAP[i][j] = num++;
    function_B(5, arrHEAP); // prints random data
    function_C(arrHEAP); // prints correctly, works

    // allocating STACK space for 2D array and initialising
    int arrSTACK[2][5] = {{100, 200, 300, 400, 500},{600,700,800,900,1000}};
    function_B(5, arrSTACK); // prints correctly, works
    //function_C(arrSTACK); // if I were to run this it crashes the program, why?
}
void function_B(int size, int (*arr)[size])
{
    for(int i=0;i<2;i++)
        for(int j=0;j<5;j++)
            printf("HEAP row is %d, value is %d:\n", i, arr[i][j]);
}
void function_C(int **arr)
{
    for(int i=0;i<2;i++)
        for(int j=0;j<5;j++)
            printf("HEAP row is %d, value is %d:\n", i, arr[i][j]);
}
int main()
{
    function_A(1);
}

Array/Pointer Conversion数组/指针转换

The defect in understand you have surrounds the use of arrays and the use of pointers.理解你的缺陷围绕着数组的使用和指针的使用。 In C, an array is a distinct type of object.在 C 中,数组是一种不同类型的对象。 One of which that causes confusion is that an array is converted to a pointer to its first element on access.其中一个导致混淆的原因是数组在访问时被转换为指向其第一个元素的指针。 (array/pointer conversion) This is governed by C11 Standard - 6.3.2.1 Other Operands - Lvalues, arrays, and function designators(p3) (note the 4 exceptions where array/pointer conversion does not occur) (数组/指针转换)这受C11 标准 - 6.3.2.1 其他操作数 - 左值、数组和函数指示符(p3)的约束(注意不发生数组/指针转换的 4 个例外)

The key here is type .这里的关键是type When you declare a 2D array, eg当你声明一个二维数组时,例如

int arrSTACK[2][5] = {{100, 200, 300, 400, 500},{600,700,800,900,1000}};

On access it will be converted to a pointer -- but what type?访问时,它将被转换为指针——但什么类型? A 2D array in C is an array of 1D arrays. C 中的二维数组是一维数组的数组。 Array/pointer conversion only applies to the first level of indirection.数组/指针转换仅适用于第一级间接。 So on access arrSTACK is converted to a pointer to array int[5] .所以在访问时arrSTACK被转换为指向数组int[5]的指针 So its type is int (*)[5] .所以它的类型是int (*)[5] Since type controls pointer arithmetic arrSTACK + 1 advances five-integer values so that it points to the beginning of the second 1D array that makes up arrSTACK (the second row)由于类型控制指针算术arrSTACK + 1推进五个整数值,以便它指向组成arrSTACK的第二个一维数组的arrSTACK (第二行)

Pointers指针

int **arrHEAP declares a single pointer. int **arrHEAP声明了一个指针。 A pointer-to-pointer-to int .指向int的指针 It has nothing to do with an array.它与数组无关。 However a pointer-to-pointer can be indexed as you would index a 2D array to address the individual integers stored in memory.但是,可以对指向指针的指针进行索引,就像索引 2D 数组以寻址存储在内存中的单个整数一样。 That is the only similarity between the 2D array and the object created by allocating storage for pointers and then allocating storage for integers and assigning the starting address for each block holding integers to one of the pointers you have allocated.这是 2D 数组与通过为指针分配存储空间然后为整数分配存储空间并将保存整数的每个块的起始地址分配给您已分配的指针之一而创建的对象之间的唯一相似之处。 Here there is no guarantee that all elements of arrHEAP are contiguous in memory as they are with a 2D array.这里不能保证arrHEAP所有元素在内存中都是连续的,就像它们在二维数组中一样。

So let's look at the difference in how pointer arithmetic works with arrHEAP .那么让我们看看指针算法如何与arrHEAP工作的不同之arrHEAP When you dereference arrHEAP , a pointer-to-pointer (eg arrHEAP[0] ) What type results from the dereference?当您取消引用arrHEAP ,一个指向指针的指针(例如arrHEAP[0] ) 取消引用会产生什么类型? If you had a pointer-to-pointer-to int and you dereference it you are left with pointer-to int .如果您有一个指向int的指针,并且您取消了对它的引用,那么您将剩下指向int指针 So with the array, the dereference resulted in the type pointer-to int[5] , but with arrHEAP[0] the result is simply a pointer-to int (no 5 -- it's just a pointer to int ).因此,对于数组,取消引用导致类型为指向int[5]指针,但对于arrHEAP[0] ,结果只是指向int指针(没有5 - 它只是指向int的指针)。 So how does pointer arithmetic differ?那么指针算法有什么不同呢? arrSTACK + 1 advances the pointer by 5 * sizeof(int) bytes ( 20 -bytes). arrSTACK + 1使指针前进5 * sizeof(int)字节( 20字节)。 With arrHEAP + 1 advances only to the next pointer in your allocated block of pointers (1-pointer 8 -bytes).使用arrHEAP + 1仅前进分配的指针块中的下一个指针(1 指针8字节)。

That is why you cannot pass one to the other function.这就是为什么您不能将一个传递给另一个函数的原因。 The function expecting the array understands arrSTACK[0] and arrSTACK[1] being 20 -bytes apart, while with the pointer arrHEAP[0] and arrHEAP[1] are only 8 -bytes apart.期望数组的函数理解arrSTACK[0]arrSTACK[1]相隔20字节,而指针arrHEAP[0]arrHEAP[1]仅相隔8字节。 This is the crux of the pointer-incompatibility warnings and errors you generate.这是您生成的指针不兼容警告和错误的症结所在。

Then there is the lack of guarantee that all values of arrSTACK being sequential in memory.然后无法保证arrSTACK所有值在内存中都是连续的。 You know that arrSTACK[1] is always 20-byes from the beginning of the array.您知道arrSTACK[1]总是从数组的开头开始 20 arrSTACK[1] With arrHEAP the first allocated pointer has no guaranteed relationship with the other from an adjacency standpoint.对于arrHEAP ,从邻接的角度来看,第一个分配的指针与另一个没有保证的关系。 They can later be replaced or reallocated.它们以后可以被替换或重新分配。

What this means is if you try and provide arrSTACK to function_C(int **arr) , the complier will generate a warning for incompatible pointer types -- because they are.这意味着如果您尝试将arrSTACK提供给function_C(int **arr) ,编译器将为不兼容的指针类型生成警告——因为它们是。 Conversely, if you attempt to provide arrHEAP to function_B(int size, int (*arr)[size]) it will likewise issue a warning due to incompatible pointer types again -- because they are.相反,如果您尝试将arrHEAP提供给function_B(int size, int (*arr)[size])它同样会由于不兼容的指针类型再次发出警告 - 因为它们是。

Even if how the object and the array are used in the other function would seem like it would work because you are essentially indexing both in the same way, the compiler cannot let one incompatible type through -- that's not the compilers job.即使对象和数组在另一个函数中的使用方式看起来会起作用,因为您基本上以相同的方式索引两者,编译器也不能让一个不兼容的类型通过——这不是编译器的工作。

The compiler can only base its operation on the promise you made to it when you wrote your code.编译器只能根据您在编写代码时对其做出的承诺进行操作。 For function_B(int size, int (*arr)[size]) you promised you were sending a 2D array of 1D arrays containing 5 int .对于function_B(int size, int (*arr)[size])你承诺你发送一个包含5 int的一维数组的二维数组。 With function_C(int **arr) , you promised the compiler you would provide pointer-to-pointer-to int .使用function_C(int **arr) ,您向编译器承诺您将提供指向int的指针 When the compiler sees you are attempting to pass the wrong object as a parameter, it will warn, and you should heed that warning, because the start of the 2nd block of integers in arrHEAP isn't guaranteed to be 6 int away from the beginning of arrHEAP -- and it won't be found there.当编译器发现您试图将错误的对象作为参数传递时,它会发出警告,您应该注意该警告,因为不能保证arrHEAP第二个整数块的开头与开头相距 6 个int arrHEAP - 它不会在那里找到。

In void function_B(int size (int (*arr)[size]) , arr points to a place where there are some number of rows of some number of int . To know where any row is, the compiler needs to know how many int are in each row. For example, with 10 rows of 12 int , row 3 starts after 3•12 int .void function_B(int size (int (*arr)[size])arr指向的地方有一定数量的行和一定数量的int 。要知道任何行在哪里,编译器需要知道有多少个int在每一行中。例如,有 10 行 12 int ,第 3 行在 3•12 int之后开始。

In void function_C(int **arr) , arr points to a place where there are pointers to rows of int .void function_C(int **arr)arr指向有指向int行的指针的地方。 To know where any row is, the compiler merely loads one of those pointers.要知道任何行在哪里,编译器只加载这些指针之一。 For example, row 3 starts where the pointer arr[3] points.例如,第 3 行从指针arr[3]指向的位置开始。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM