C++:通过函数修改数组

C++: Modifying array via function

本文关键字:修改 数组 函数 C++      更新时间:2023-10-16

这已经困扰了我一个多星期了。我可能错过了解释这一点的帖子,但我读过一堆帖子/文章,它们要么没有告诉我任何我不知道的事情,要么超出了我的头脑。

考虑一下:

#include<iostream>
using namespace std;
void poo(int *currArray){
cout << "*currArray: " << currArray << endl;
int * blah = new int [5];
cout << "blah: " << blah << endl;
currArray = blah;
cout << "currArray after switch " << currArray << endl;
}
int main(){
int * foo = new int [5];
cout << "foo: " << foo << endl;
poo(foo);
cout << "foo after poo " << foo << endl;
}

这会产生输出:

foo: 0x7ffdd5818f20
*currArray: 0x7ffdd5818f20
blah: 0x7ffdd5818ef0
currArray after switch 0x7ffdd5818ef0
foo after poo 0x7ffdd5818f20

如您所见,虽然地址切换发生在函数内部,但它不会延续到主函数。我也知道这是在泄漏内存,但我认为这与这个问题无关。

我的理解是数组本质上指向其第一个元素的地址。我的理解也是数组是"通过引用"传递的。即,传递数组的地址。

那么为什么poo后地址切换不持续呢?我不是把currArray的指针(foo的别名)改成blah的指针吗?

什么是指针?它是一个包含内存地址的变量。它可以被复制,它可以被引用,你甚至可以获取它的地址。因此,当您将函数声明为void poo(int *currArray)时,将传递的参数的值复制到变量currArray中。所以currArray现在包含与foo相同的地址。但是,当您更改currArray的值时,您只会更改currArray的值,因为它是副本而不是引用。

您可以看到 Carl 关于如何通过引用传递指针的答案。修改foo的另一种方法是将foo的地址传递给函数。由于现在您知道foo在内存中的位置,因此您可以通过取消引用它并为其分配blah地址来更改其值。当然,您应该首先释放foo占用的内存(通过currArray),因为如果您不这样做,您将无法执行此操作,并且会产生内存泄漏。

下面是示例代码:

#include<iostream>
void poo(int **currArray)
{
std::cout << "currArray: " << *currArray << std::endl;
int *blah = new int[5];
std::cout << "blah: " << blah << std::endl;
delete[] *currArray; // clean after ourselves
*currArray = blah;
std::cout << "currArray after switch " << *currArray << std::endl;
}
int main()
{
int *foo = new int[5];
std::cout << "foo: " << foo << std::endl;
poo(&foo);
std::cout << "foo after poo " << foo << std::endl;
delete[] foo; // clean after ourselves
}

演示:https://ideone.com/4UWYlG

foo: 0x560b3e9ffc20
currArray: 0x560b3e9ffc20
blah: 0x560b3ea00c50
currArray after switch 0x560b3ea00c50
foo after poo 0x560b3ea00c50

这里需要注意的重要一点是,由于指针是对象,因此在将foo指针传递给poo时,您是通过值获取指针的。所以函数内的赋值实际上只应用于局部参数currArray。为了使它与范围之外的指针一起持久存在,您必须声明您的函数,例如:void poo(int *& currArray);尽管这不适用于数组。这意味着它是对指向 int 的指针的引用。否则,指针按值传递。

话虽如此,您正在尝试做的是,将指针分配给函数poo内的本地堆栈对象不是很好,因为一旦函数返回,您的另一个指针将指向被破坏的内容,这将导致未定义的行为。

你应该考虑阅读更多关于任何一本好的C++书中的指针、参考文献和论证传递。

虽然数组的地址被传递是正确的,但指针本身是按值传递的,因此函数内的本地指针指向相同的地址,但它与 main 中的指针不同。

此外,您应该强烈考虑使用std::vector而不是原始数组,因为这将更容易使用、维护并且没有太大的性能差异(如果有的话)。

下面是一段工作代码:

void poo(int *& currArray){
/* ... */
}
int main(){
int foo[5];
int* doo = foo;
cout << "foo: " << doo << endl;
poo(doo);
cout << "foo after poo " << doo << endl;
}

您必须制作 doo 的原因是,您不能将非常量左值绑定到数组本身foo。如果你让它变得 const,你就无法在该函数中分配。如果您堆分配了该数组,那很好,因为 foo 的类型会int*

关于按值按引用传递存在一些混淆。对于数组尤其如此。

我不会在这里打扰你的整个画面,但我会尽量简洁地解决你的误解:

  1. C++所有参数都是按值传递的,除非类型包含引用限定符&&&例如void foo(int& a)
  2. 在您的示例中,没有&因此没有通过引用传递任何内容
  3. 我认为这种关于通过引用传递数组的混淆来自:您使用指向数组开头的指针将数组"传递"给函数。重要提示:您传递的不是数组本身,而是指向其开头的指针。但是,在函数内部,您可以通过*[]运算符访问数组的元素。它们将使您能够访问实际的数组元素,而不是它们的副本。这在某种程度上感觉就像通过引用传递数组。因此混乱。

因此,您可以修改函数内部的数组元素,您将在main中看到效果。但是,如果修改指向数组的指针,则不会在main中看到任何效果,因为该指针是按值传递的。

currArray

不是别名,它是一个函数本地对象,其值已通过参数传递机制初始化为传递的值,并且函数中发生的事情保留在该函数中。你可以声明它是对指针的引用,然后使用它来修改传递的任何实际指针(但不是数组),但同样,这就是引用的工作方式,而不是某种参数魔法。