问题描述
我对使用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>,它们只是具有不同的类型(&arr
与int (*)[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
的第一个元素的指针,而不是数组的副本。
- 不同的指针类型可能具有不同的表示形式,因此
myvla
和arr
的值可能不是按位相同的,但最终它们确实表示相同的位置。
当您尝试更改函数内部的地址(指针存储的值的类型!)时,您可能会理解其中的区别。
//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;
}