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

时间:2018-06-09 20:46:22

标签: c++ pointers scope reference

这一直困扰我一个多星期了。我可能错过了解释这个问题的帖子,但是我已经阅读了很多帖子/文章,他们要么不告诉我任何我不知道的事情,要么告诉我。

考虑一下:

#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的指针({1}}的指针更改为foo的指针?

4 个答案:

答案 0 :(得分:2)

什么是指针?它是一个包含内存地址的变量。它可以被复制,可以被引用,你甚至可以获取它的地址。因此,当您将函数声明为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

答案 1 :(得分:1)

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

话虽如此,你正在尝试做的事情,在函数poo内部指定一个本地堆栈对象的指针并不是很好,因为一旦函数返回你的另一个指针就会指向某个东西被破坏,这将导致不明确的行为。

您应该考虑在任何good C++ book中阅读有关指针,引用和参数传递的更多信息。

虽然你是正确的,数组的地址是通过的,指针本身是通过值传递的,所以函数内的本地指针指向同一个地址,但它的指针不同于主指针中的指针。

此外,您应该强烈考虑使用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的原因是你不能将非const左值绑定到数组foo本身。如果你使它成为常量,你就无法在该函数中进行分配。如果你分配了那个数组,那就好了,因为foo的类型是int*

答案 2 :(得分:1)

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

我不会在这里打扰你的全貌,但我会尽可能地解决你的误解:

  1. 在C ++中,除非类型包含引用限定符&&&,否则所有参数都按值传递。 void foo(int& a)
  2. 在您的示例中,没有&因此没有通过引用传递任何内容
  3. 我认为通过引用传递数组的这种混淆来自于:您使用指向数组开头的指针,将数组“传递”到函数中。重要提示:您没有传递数组本身,而是指向其开头的指针。但是,在函数内部,您可以通过*[]运算符访问数组的元素。它们将允许您访问实际数组元素,而不是它们的副本。这不知何故感觉就像通过引用传递数组一样。因此混乱。
  4. 因此,您可以修改函数内部的数组元素,您将在main中看到效果。但是如果你修改指向数组的指针,你就不会在main中看到任何效果,因为该指针是按值传递的。

答案 3 :(得分:0)

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