【问题标题】:Explain how this C++ function returns an array解释这个 C++ 函数如何返回一个数组
【发布时间】:2013-03-10 19:04:56
【问题描述】:

我在网上找到了这段代码http://www.cplusplus.com/forum/beginner/6644/#msg30551,它应该从 c++ 函数返回一个数组。我想解释一下这个函数是如何在内存分配、堆栈、堆、指针等方面运行的。

int *f(size_t s){
    int *ret=new int[s];
    for (size_t a=0;a<s;a++)
        ret[a]=a;
    return ret;
}

【问题讨论】:

  • 它不返回数组。它返回一个指向动态分配数组的第一个元素的指针。

标签: c++ arrays stack return-value function-pointers


【解决方案1】:

我。

int *ret=new int[s];

1。在堆栈上为 ret 分配内存 - 这是一个 int 指针
2.在堆上分配大小为s * sizeof(int)的连续内存
3. 使ret 指向已分配内存的第一个元素(来自2.)


二。

for (size_t a=0;a<s;a++)
    ret[a]=a;
  1. 在堆栈上为a分配内存
  2. 循环遍历 I. 中分配的内存,为每个元素分配值
  3. for 语句结束后,a 不再可访问(仅在 for 中可用)

三。

return ret;

返回ret 指针的副本,它指向在 I. 中创建的数组的第一个元素,在 II 中初始化。

在return 之后,ret 被“销毁”。


这个函数的调用者不能忘记释放(释放)这个内存,调用delete[]。

例如:

int * my_array = f( 6 );
// do sth with my_array
delete[] my_array;

【讨论】:

    【解决方案2】:

    实际上,该函数不会返回ints 的数组(即int[N])。它返回的是一个指向int (int *) 的指针。事实证明,这个指针指向 s 类型为 int 的元素数组的第一个元素。

    注意内存是用new分配的:

    int *ret = new int[s];
    

    因此ret指向的ints数组具有动态存储时长。除其他外,这意味着

    1) 编译器不会自动调用每个数组元素的析构函数。 (在这种情况下,这不是问题,因为元素的类型是 int,但如果元素是具有非平凡析构函数的类类型,则可能是 where。)

    2) 编译器不会自动释放分配的内存。

    相比之下,请考虑以下代码:

    void g() {
        int p[10]; // allocates 10 integer in the stack
        // use p ...
    }
    

    当g 终止时,编译器将执行上述操作。为此,必须在编译时设置数组的大小(本例中为 10)。如果您在编译时不知道大小,则需要new,如原始代码中所示。

    对于动态分配的数组,程序员有责任确保在不再需要数组时执行上述两个操作。为此,您必须致电delete[]:

    delete[] p; // where p is a `int*` with the same value as `ret`
    

    在实践中,这比看起来更困难,因为有可能 抛出异常。例如,考虑这段代码

    void foo() {
        int* p = f(10); // where f is in the question
        // ... use the array pointed by p
        a_function_that_might_throw();
        delete[] p;
    }
    

    如果a_function_that_might_throw 确实抛出异常,则执行 永远不会达到删除p 的地步。在这种情况下,内存 由new 分配的(在f 内部)将不会被释放(它泄漏),直到 程序终止。

    为了避免这个问题,最好不要使用原始指针(例如int*) 使用智能指针(例如 std::unique_ptr 或 std::shared_ptr)。

    最后,默认情况下new分配的内存是堆内存。然而, 你可以改变这种行为。

    【讨论】:

      【解决方案3】:
      int *ret=new int[s];
      

      这一行定义了一个名为ret 的int*,具有自动存储持续时间。它使用 new-expression new int[s] 返回的指针初始化 ret。这个new-expression创建了一个具有动态存储持续时间的sints 数组,返回一个指向该数组中第一个元素的指针。

      所以我们现在有两个对象:具有自动存储持续时间的 int* 和具有动态存储持续时间的 int[]。

      for (size_t a=0;a<s;a++)
      

      这是for 声明。 for-init-statement 定义了一个名为a 的size_t 对象并将其初始化为0。条件 检查a 是否小于s。最后的表达式递增a。这意味着a 在[0, s) 范围内循环。

      ret[a]=a;
      

      这会将a 的值分配给ret 中的ath 元素。也就是说,ret[0] 的值为 0,ret[1] 的值为 1,以此类推。

      a 对象现在已被销毁,因为它具有自动存储持续时间,并且我们已到达其范围的末尾(for 语句)。

      return ret;
      

      这将返回ret 的值,您记得它是int*。因此函数的返回值是一个int*,指向动态分配数组的第一个元素。

      ret 对象现在已被销毁,因为它具有自动存储持续时间,并且我们已到达其范围的末尾(f 函数)。请注意,这只是函数内部的指针。动态分配的数组依然存在,返回的指针依然指向它。

      您必须在稍后的某个时间点,记住返回的指针delete[]。

      【讨论】:

        【解决方案4】:
        int *ret =  new int[s];
        

        这会动态地(在堆上的~)分配一个s 整数数组,并在ret 中存储一个指向它的指针(实际上是指向它的第一个元素)。

        我相信函数的其余部分很简单。

        所以该函数将一个指针返回给一个动态分配的数组。这并不安全。如果调用者没有存储返回值,然后在上面调用delete[],它就会泄漏。

        【讨论】:

          猜你喜欢
          • 2018-01-01
          • 2012-04-01
          • 1970-01-01
          • 2021-05-12
          • 2023-04-08
          • 2014-09-16
          • 1970-01-01
          • 2023-01-18
          • 1970-01-01
          相关资源
          最近更新 更多