简体   繁体   English

通过引用或值传递二维指针数组

[英]Pass by reference or value for 2D pointer array

I have a question about using pass-by-reference for 2D arrays (VLA variants) in C. It seems most of the examples demonstrated, like #2 here: How to pass 2D array (matrix) in a function in C?我有一个关于在 C 中对 2D 数组(VLA 变体)使用传递引用的问题。似乎大多数示例都进行了演示,例如这里的 #2: How to pass 2D array (matrix) in a function in C? shows that you don't have to use pass-by-reference convention.表明您不必使用传递引用约定。 Just to show my example:只是为了展示我的例子:

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

void assign(double** testMatrix, int* dim){
   for(int row=0; row < *dim; ++row){
       for(int column=0; column< *dim; ++column){
           testMatrix[row][column] = 0;
       }
   }
   
}

int main(void) {
   
   int dim = 200;
   
   double** testMatrix = malloc(sizeof(double*) * dim);
   for(int i=0; i < dim; ++i){
       testMatrix[i] = malloc(sizeof(double) * dim);
   }
   
   assign(testMatrix, &dim);
   
   //deallocate test matrix
   for(int i=0; i< dim; ++i){
       free(testMatrix[i]);
   }
   free(testMatrix);
   
   return 0;
}

the above sample code assigning the 2D array without using conventions for pass-by-reference, like the sample below (see assign function with the &):上面的示例代码在不使用传递引用约定的情况下分配二维数组,如下面的示例(请参阅带有 & 的赋值函数):

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

void assign(double*** testMatrix, int* dim){
    for(int row=0; row < *dim; ++row){
        for(int column=0; column< *dim; ++column){
            (*testMatrix)[row][column] = 0;
        }
    }
    
}

int main(void) {
    
    int dim = 200;
    
    double** testMatrix = malloc(sizeof(double*) * dim);
    for(int i=0; i < dim; ++i){
        testMatrix[i] = malloc(sizeof(double) * dim);
    }
    
    assign(&testMatrix, &dim);
    
    //deallocate test matrix
    for(int i=0; i< dim; ++i){
        free(testMatrix[i]);
    }
    free(testMatrix);
    
    return 0;
}

My question is how is the first example's 2D array modified without passing the reference of the array?我的问题是如何在不传递数组引用的情况下修改第一个示例的二维数组?

For starters you do not have a two-dimensional array and moreover a VLA array.首先,您没有二维数组,而且没有 VLA 数组。 You have a pointer of the type double ** that points to an allocated memory.您有一个double **类型的指针,指向已分配的内存。

Within this function在这个函数内

void assign(double** testMatrix, int* dim){
   for(int row=0; row < *dim; ++row){
       for(int column=0; column< *dim; ++column){
           testMatrix[row][column] = 0;
       }
   }
   
}

the pointer itself is not changed.指针本身没有改变。 It is the pointed data that are changed and the pointed data are passed to the function by reference using the pointer declared in main.改变的是指向的数据,并且指向的数据使用 main 中声明的指针通过引用传递给函数。

Within this function在这个函数内

void assign(double*** testMatrix, int* dim){
    for(int row=0; row < *dim; ++row){
        for(int column=0; column< *dim; ++column){
            (*testMatrix)[row][column] = 0;
        }
    }
    
}

there is again the passed pointer by referenced is not changed.再次引用传递的指针没有改变。 So there is no sense to pass the original pointer by reference.所以通过引用传递原始指针是没有意义的。

Here is a demonstrative program that shows when you need to pass a pointer by reference to change it itself.这是一个演示程序,显示何时需要通过引用传递指针以更改它本身。

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

int change( int **p )
{
    int *tmp = realloc( *p, 2 * sizeof( int ) );
    int success = tmp != NULL;
    
    if ( success )
    {
        tmp[1] = 2;
        
        *p = tmp;
    }
    
    return success;
}

int main(void) 
{
    int *p = malloc( sizeof( int ) );
    *p = 1;
    
    printf( "p[0] = %d\n", p[0] );
    
    if ( change( &p ) )
    {
        printf( "p[0] = %d, p[1] = %d\n", p[0], p[1] );
    }
    
    free( p );

    return 0;
}

The program output is程序输出是

p[0] = 1
p[0] = 1, p[1] = 2

That is within the function change the pointer p itself declared in main is changed because it is passed to the function by reference.也就是说,在函数change ,在 main 中声明的指针p本身发生了更改,因为它是通过引用传递给函数的。

The code double** testMatrix = malloc(sizeof(double*) * dim);代码double** testMatrix = malloc(sizeof(double*) * dim); creates a pointer to a pointer to a double and sets it to point to allocated storage.创建一个指向double的指针并将其设置为指向已分配的存储。 The loop that follows it fills in the allocated storage with pointers to double .跟随它的循环用指向double指针填充分配的存储。

Then the function call assign(testMatrix, &dim);然后函数调用assign(testMatrix, &dim); passes that first pointer to assign .将第一个指针传递给assign

Since assign has the address of the allocated storage, it can access the pointers in it.由于assign具有分配存储的地址,因此可以访问其中的指针。 Since it has those pointers, it can access the storage they point to.因为它有这些指针,所以它可以访问它们指向的存储。 This answers the question “how is the first example's 2D array modified…”: When you pass a pointer to something, you are passing a means of accessing the thing.这回答了“第一个示例的二维数组是如何修改的……”的问题:当您传递指向某物的指针时,您正在传递一种访问该事物的方法。

In fact, passing a pointer to something is passing a reference to something.实际上,传递指向某物的指针就是传递对某物的引用。 The pointer refers to the thing.指针指向事物。 (C++ introduce a new feature it called a “reference,” and it is a sort of automatically managed reference. But any way of referring to a thing—giving its address, its name, a description of where to find it, a bibliographic citation, a URL, or a pointer to a structure that has such information—is a kind of reference.) (C++ 引入了一个称为“引用”的新特性,它是一种自动管理的引用。但是任何引用事物的方式——给出它的地址、名称、在哪里找到它的描述、参考书目、URL 或指向具有此类信息的结构的指针是一种引用。)

Thus, in passing testMatrix to assign, you passed the value of testMatrix , which is also a reference to the storage it points to, and that storage contains references (in the form of pointers) to the storage for the double values.因此,在传递testMatrix以进行分配时,您传递了testMatrix的值,该值也是对其指向的存储的引用,并且该存储包含对double值的存储的引用(以指针的形式)。

First of all, just for pedantry's sake, C passes all function arguments by value, period.首先,只是为了学究,C 通过值和句点传递所有函数参数。 Sometimes those values are pointers, and we can modify things through those pointers, which is what most of us mean when we talk about "pass by reference" in C, but strictly speaking what we are doing is passing a pointer by value .有时这些值是指针,我们可以通过这些指针修改事物,这就是我们大多数人在 C 中谈论“通过引用传递”时的意思,但严格来说,我们所做的是通过传递指针。

Clear as mud?清如泥? Okay.好的。

Next, your testMatrix is not a 2D array - it's a pointer to the first of a sequence of pointers.接下来,您的testMatrix不是二维数组 - 它是指向指针序列中第一个的指针。 In memory it looks something like this:在内存中它看起来像这样:

            int **      int *                    int
            +---+       +---+                    +---+
testMatrix: |   | ----> |   | testMatrix[0] ---> |   | testMatrix[0][0]
            +---+       +---+                    +---+
                        |   | testMatrix[1] --+  |   | testMatrix[0][1]
                        +---+                 |  +---+
                         ...                  |   ...
                                              |   
                                              |  +---+
                                              +->|   | testMatrix[1][0]
                                                 +---+ 
                                                 |   | testMatrix[1][1]
                                                 +---+
                                                  ...

The expression testMatrix has pointer type ( int ** ), not array type.表达式testMatrix具有指针类型( int ** ),而不是数组类型。 When you call assign you are passing a pointer value, which is what it expects:当您调用assign您正在传递一个指针值,这是它所期望的:

void assign(double** testMatrix, int* dim)

If you declare a true 2D array such as如果您声明一个真正的二维数组,例如

int arr[2][2];

you get this in memory:你在内存中得到这个:

      int
      +---+
 arr: |   | arr[0][0]
      +---+ 
      |   | arr[0][1]
      +---+
      |   | arr[1][0]
      +---+
      |   | arr[1][1]
      +---+

Unless it is the operand of the sizeof or unary & operators, or is a string literal used to initialize a character array in a declaration, an expression of type "N-element array of T " is converted ("decays") to an expression of type "pointer to T " and the value of the expression will be the address of the first element of the array.除非它是sizeof或一元&运算符的操作数,或者是用于在声明中初始化字符数组的字符串文字,否则类型为“ T N 元素数组”的表达式将转换(“衰减”)为表达式类型为“指向T指针”,表达式的值将是数组第一个元素的地址。

The expression arr has type "2-element array of 2-element array of int ";表达式arr类型为“ int的二元数组的二元数组”; unless it is the operand of the sizeof or unary & operators, it "decays" to an expression of type "pointer to 2-element array of int ", or int (*)[2] .除非它是sizeof或一元&运算符的操作数,否则它会“衰减”为“指向int 2 元素数组的指针”或int (*)[2]类型的表达式。 If you pass the expression arr to a function, the function will actually receive a pointer to the first element of the array, not a copy of the array.如果将表达式arr传递给函数,该函数实际上将接收指向数组第一个元素的指针,而不是数组的副本。

Again, this isn't really "pass by reference", we're passing a pointer by value .同样,这并不是真正的“按引用传递”,我们是按值传递指针。 However, the practical effect is that the formal parameter in the function can be subscripted, and any changes to array elements in the function are reflected in the array passed by the caller.但是,实际效果是函数中的形参可以下标,函数中数组元素的任何变化都会反映在调用者传递的数组中。

This is why you almost never need to use the & operator when passing an array expression as a function parameter - the function is already receiving the address of the first element of the array.这就是为什么在将数组表达式作为函数参数传递时几乎不需要使用&运算符的原因 - 函数已经在接收数组的第一个元素的地址。 Yes, the address of an array is the same as the address of its first element, so the value of arr and &arr would be the same 1 , they'd just have different types ( int (*)[2] vs. int (**)[2] ).是的,数组的地址与其第一个元素的地址相同,因此arr&arr将是相同的1 ,它们只是具有不同的类型( int (*)[2]int (**)[2] )。

So if I call a function foo with所以如果我调用一个函数foo

foo( arr, 2 );

then the function prototype will be那么函数原型将是

void foo( int (*arr)[2], int rows )

In the context of a function parameter declaration, T a[N] and T a[] are identical to T *a - all three declare a as a pointer to T , so you can also write that declaration as在函数参数声明的上下文中, T a[N]T a[]T *a相同 - 所有三个都a a 声明为指向T的指针,因此您也可以将该声明写为

void foo( int arr[][2], int rows )

Again, this is only true for function parameter declarations.同样,这适用于函数参数声明。

VLAs work mostly like regular arrays - what you can do in the function prototype is write VLA 的工作方式主要类似于常规数组 - 您可以在函数原型中执行的操作是编写

void foo( int rows, int cols, int arr[rows][cols] )

This works if you declare a VLA like如果您声明一个 VLA,这会起作用

int rows = get_rows();
int cols = get_cols();
int myvla[rows][cols];

foo( rows, cols, myvla );

Again, what foo receives is a pointer to the first element of myvla , not a copy of the array.同样, foo接收的是指向myvla的第一个元素的myvla ,而不是数组的副本。


  1. Different pointer types may have different representations, so the values of arr and &arr may not be bitwise identical, but they do ultimately represent the same location.不同的指针类型可能有不同的表示,因此arr&arr的值可能按位不同,但它们最终表示相同的位置。

You might understand the difference when you try to alter the address(which is the kind of value a pointer store!) inside the function.当您尝试更改函数内的地址(这是指针存储的值类型!)时,您可能会理解其中的区别。

//here you create a copy of the matrix address, so changing 
//this address here wont change the real address
void f1(double **m) 
{
     //m is a copy of the address
     m = malloc(sizeof(double*)*200);
}
//Here we change the address of what as passed, it will assign a new address
//to the matrix that was passed
void f2(double ***m)
{
    //when you dereference with one * you get the real address and any changes
    // on *m will reflect on the parameter passed
    *m = malloc(sizeof(double*)*200);
}

int main(void) {

  int dim = 200;
  double** testMatrix = malloc(sizeof(double*) * dim);
  for(int i=0; i < dim; ++i){
    testMatrix[i] = malloc(sizeof(double) * dim);
  }
  double **other = testMatrix;
  f1(testMatrix);
  printf("point to the same place ? %d\n", other == testMatrix); 
  f2(&testMatrix);
  printf("point to the same place ? %d\n", other == testMatrix);       
  return 0;
}

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

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