【问题标题】:C++: Modifying array via functionC++:通过函数修改数组
【发布时间】:2018-06-09 20:46:22
【问题描述】:

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

考虑一下:

#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的指针?

【问题讨论】:

  • 好吧,顺便说一句,如果你打算做的工作,你会有一个指向本地对象的指针,当你尝试cout它时,它会超出范围,所以这不是好的。一般不要让参数或返回值指向生命周期受限于本地范围的本地对象。
  • 哦,我明白你在说什么。但是即使我使用new在堆上分配fooblah,我仍然有同样的问题。我应该编辑我的问题以包含它吗?
  • newdelete 在现代 C++ 中通常应该避免使用,但是是的,包含这将是一件好事。我很快就会发布答案。
  • 数组可能feel 像传递引用但指针不是。指针是整数类型。这意味着它只是一个保存地址的整数。当您调用该方法时,该整数(指针)的副本将传递给该函数。在 C 和 C++ 中,数组总是作为指针传递。
  • @DillonChan 当您执行此操作时,您将currArrays 指针的副本传递给函数void poo(int *currArray)(请记住,按值传递)。为了在currArray 中进行任何更改,您需要引用void poo(int* &amp;currArray) 或指针地址void poo(int** currArray)

标签: c++ pointers scope reference


【解决方案1】:

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

【讨论】:

    【解决方案2】:

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

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

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

    虽然传递数组的地址是正确的,但指针本身是按值传递的,因此函数内部的本地指针指向相同的地址,但它与 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 的原因是你不能将非 const 左值绑定到数组 foo 本身。而且,如果您将其设为 const,则无法在该函数中进行分配。如果您堆分配该数组,那会很好,因为 foo 的类型是int*

    【讨论】:

    • 您错过了第二个选项:void poo(int** currArray) 请参阅:ideone.com/bJvBhi
    • 感谢您的回答卡尔。您能否详细说明无法将非常量左值绑定到 foo 的错误?这种绑定发生在哪里?起初,我以为是因为foo 不是int *&amp; 类型,所以做了一个临时指针并试图分配给currArray,这显然是一个禁忌。但是doo 也没有正确的类型,为什么它与堆分配的变量一起工作?
    • 它适用于堆,因为堆分配对象的类型是int*,这是函数所采用的。 int foo[5] 在堆栈上,它的类型不是int*,而是int[5]。因此,如果您的函数将指针指向长度为 5 的数组,那么就可以了。这就是为什么我们将doo 设为int* 以允许传递参数。
    【解决方案3】:

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

    我不会在这里用整个图片来打扰你,但我会尽可能简洁地解决你的误解:

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

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

    【讨论】:

      【解决方案4】:

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

      【讨论】:

        猜你喜欢
        • 2015-08-06
        • 2020-07-25
        • 1970-01-01
        • 1970-01-01
        • 2012-06-04
        • 1970-01-01
        • 2015-05-19
        • 2021-10-14
        • 2010-12-21
        相关资源
        最近更新 更多