通过引用或2D指针数组的值传递

问题描述

我对使用C中的2D数组(VLA变体)使用传递引用存在疑问。似乎已演示了大多数示例,例如此处的#2:How to pass 2D array (matrix) in a function in C?表明您不必使用按引用传递约定。只是为了展示我的例子:

#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;
}

上面的示例代码在不使用传递引用约定的情况下分配2D数组,例如下面的示例(请参见带有&的Assign函数):

#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;
}

我的问题是如何在不传递数组引用的情况下修改一个示例的2D数组?

解决方法

对于初学者,您没有二维数组,而且没有VLA数组。您有一个double **类型的指针指向已分配的内存。

在此功能之内

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

指针本身未更改。指向的数据被更改,并且指向的数据通过使用main中声明的指针通过引用传递给函数。

在此功能之内

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

再次存在通过引用传递的指针不变的情况。因此,没有必要通过引用传递原始指针。

这是一个演示程序,它显示何时需要通过引用传递指针来更改指针本身。

#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;
}

程序输出为

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

在函数change中,在main中声明的指针p本身已更改,因为它是通过引用传递给函数的。

,

代码double** testMatrix = malloc(sizeof(double*) * dim);创建一个指向double的指针,并将其设置为指向已分配的存储。紧随其后的循环将指向double的指针填充到分配的存储中。

然后,函数调用assign(testMatrix,&dim);将第一个指针传递到assign

由于assign具有分配的存储的地址,因此它可以访问其中的指针。由于它具有这些指针,因此它可以访问它们指向的存储。这回答了“如何修改第一个示例的2D数组……”的问题:当您传递指向某物的指针时,您正在传递一种访问该物的方法。

实际上,传递指向某物的指针就是传递对某物的引用。指针指向事物。 (C ++引入了一个称为“引用”的新功能,它是一种自动管理的引用。但是任何引用事物的方式-提供其地址,名称,找到位置的描述,书目引用,URL或指向具有此类信息的结构的指针-是一种引用。)

因此,在传递testMatrix时,您传递了testMatrix的值,该值也是对其指向的存储的引用,并且该存储包含引用(以指针的形式)到double值的存储中。

,

首先,仅出于学究的缘故,C按值,句点传递 all 函数参数。有时这些值是指针,我们可以通过这些指针修改内容,这就是我们大多数人在C语言中讨论“按引用传递”时的意思,但严格来说,我们正在做的事情是按值传递指针

像泥一样清晰吗?好吧。

接下来,您的testMatrix不是2D数组-它是指向一系列指针中的第一个的指针。在内存中看起来像这样:

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

表达式 testMatrix具有指针类型(int **),而不是数组类型。调用assign时,您正在传递一个指针值,这是它期望的值:

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

如果您声明一个真实的2D数组,例如

int arr[2][2];

您在内存中得到了这个

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

除非它是sizeof或一元&运算符的操作数,否则它是用于在声明中初始化类型为 expression 的声明中的字符数组的字符串文字。 “ T的N元素数组被转换(“衰减”)为类型为“ T的指针”的表达式,该表达式的值将是数组第一个元素的地址

表达式 arr的类型为“ int的2元素数组的2元素数组”;除非它是sizeof或一元&运算符的操作数,否则它会“衰减”为“指针指向int的2元素数组的指针”或“ {{1}”的表达式}。如果将表达式int (*)[2]传递给一个函数,该函数实际上将收到一个指向数组第一个元素的指针,而不是该数组的副本。

同样,这并不是真正的“按引用传递”,我们是按值传递指针 。但是,实际效果是可以对函数中的形式参数进行下标,并且对函数中数组元素的任何更改都将反映在调用者传递的数组中。

这就是为什么在将数组表达式作为函数参数传递时几乎不需要使用arr运算符的原因-函数已经已经接收到数组第一个元素的地址。是的,数组的地址与其第一个元素的地址相同,因此&arr value 将与 1相同。 sup>,它们只是具有不同的类型(&arrint (*)[2])。

因此,如果我使用以下命令调用函数int (**)[2]

foo

那么函数原型将是

foo( arr,2 );

在函数参数声明的上下文中,void foo( int (*arr)[2],int rows ) T a[N]T a[]相同-所有三个声明T *a作为指向a的指针,所以您也可以将该声明写为

T

同样,对于函数参数声明,这仅是

VLA的工作方式主要是,就像常规数组一样-您可以在函数原型中执行写操作

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

如果您声明类似VLA,这将起作用

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

同样,int rows = get_rows(); int cols = get_cols(); int myvla[rows][cols]; foo( rows,cols,myvla ); 收到的是指向foo的第一个元素的指针,而不是数组的副本。


  1. 不同的指针类型可能具有不同的表示形式,因此myvlaarr的值可能不是按位相同的,但最终它们确实表示相同的位置。
,

当您尝试更改函数内部的地址(指针存储的值的类型!)时,您可能会理解其中的区别。

//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;
}