解释此C++函数如何返回数组

Explain how this C++ function returns an array

本文关键字:返回 数组 何返回 C++ 函数 解释      更新时间:2023-10-16

我在网上找到了这段代码 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;
}

I.

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;

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

请注意,内存分配有new

int *ret = new int[s];

因此,ret指向的int数组具有动态存储持续时间。除其他事项外,这意味着

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

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_ptrstd::shared_ptr(。

最后,默认情况下,new分配的内存是堆内存。然而您可以更改此行为。

int *ret=new int[s];

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

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

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

这是一个for的说法。for-init-语句定义了一个名为 asize_t对象,并将其初始化为 0。该条件检查a是否小于 s 。最后一个表达式递增a 。这意味着a循环的范围[0, s) .

ret[a]=a;

这会将 a 的值分配给 ret 中的第 a 个元素。也就是说ret[0]将具有值 0ret[1]将具有值 1 ,依此类推。

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

return ret;

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

ret对象现在被销毁,因为它具有自动存储持续时间,并且我们已经到达了其作用域(f函数(的尽头。请注意,这只是函数内的指针。动态分配的数组仍然存在,返回的指针仍指向它。

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

int *ret =  new int[s];

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

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

因此,该函数将返回指向动态分配数组的指针。这不是很安全;如果调用方不存储返回值,稍后对其调用delete[],它将泄漏。