为什么你想要在堆上有一个数组?

时间:2013-11-05 19:31:26

标签: c arrays memory dynamic

为什么你想要在堆上有一个数组?我的教授给了我们两个理由:

  1. 将数组传递给函数,而不是传递副本
  2. 使数组超出范围
  3. 不能通过以下方式解决这两个问题:

    1. 将指针传递给堆栈上的数组
    2. 返回数组的值而不是数组本身(即使用复制构造函数)
    3. 有人可以举例说明堆中的数组的使用位置吗?

6 个答案:

答案 0 :(得分:4)

堆中的数组用于超出函数的范围。将指针传递给堆栈上的数组仅在以后不想在之前(上部)调用者中使用它时才有效。并且你不能从函数返回一个数组,你可以返回一个指向数组的指针,但如果它是在堆栈中分配的,它将在函数返回后指向一个无效的内存位置。

第一个原因是错误的:数组永远不会通过副本传递。当您调用函数时,数组名称总是衰减为指向其第一个元素的指针,正是为了避免复制整个数组。如果您想按副本传递数组,则必须将其嵌入struct并传递struct

如果您事先不知道数组的大小,动态数组分配也很有用(尽管在C99带来可变长度数组之后不是这样 - 但是,仍然可以在堆栈上分配可变长度数组,所以你要有同样的问题)。

使用堆分配的另一个好理由是,对于非常大的数组,您可以很容易地从堆栈内存中掉出来。堆通常更大。

答案 1 :(得分:1)

C中的数组表示为引用数组数据位置的指针(它指向数组中的第一项)。对于基于堆栈的数组,数组指针和数据位于同一位置。对于堆分配的数组,数组指针位于堆栈上,并指向堆上阵列数据开始的位置。

对于第(2)点,您不能返回数组的值。返回的是数组在内存或堆栈中的位置。因此,在堆上分配它可确保在从函数返回数组时保留数据。

另一方面,std::vector在功能上像数组一样工作。这样,数组数据就在堆上分配,但管理数组的对象在堆栈上。因此,数组的生命周期由向量对象的生命周期控制。

std::vector有您描述的行为:

  1. 将值向量传递给函数会导致数据在传递给函数时被复制;

  2. 矢量数据仅适用于函数的生命周期。

  3. 从函数传递向量可能导致复制数组数据。但是,这可以使用返回值优化和R值引用等优化来避免复制。

答案 2 :(得分:0)

#include <assert.h>
#include <stdlib.h>

int * f(int* array) {
    assert(array[0] == 1); // OK

    int static_array[] = {1, 2, 3};
    //return static_array = {1, 2, 3}; //BAD: only lives in this function

    int * dynamic_array = malloc(sizeof(int) * 2);
    dynamic_array[0] = 1;
    dynamic_array[1] = 2;
    return dynamic_array; // OK: lives outside also
}

int main()
{
    int static_array[] = {1, 2, 3};
    int * returned_array;
    returned_array = f(static_array);
    assert(returned_array[0] == 1);
    free(returned_array);
}

答案 3 :(得分:0)

如果此代码在没有崩溃的情况下运行,则可以在堆栈上分配所有数组。

#include <string.h>
int main() {
    volatile char buf[1024 * 1024 * 64];
    memset(buf, 0, sizeof(buf));
}

答案 4 :(得分:0)

除非你需要让数组超出声明和初始化它的函数的范围,否则编译器可以进行一些优化,这些优化很可能最终会比程序员猜测的更有效。除非您有时间进行基准测试和实验,并且您的应用程序对性能至关重要,否则请将优化保留给编译器。

答案 5 :(得分:0)

您希望在堆上而不是堆栈上分配数组的原因:

  1. 数组非常大;
  2. 数组的生命周期超出任何一个函数的范围;
  3. 在编译时不知道数组大小, VLA要么不可用,要么在特定情况下无法使用(不能在static或文件范围内声明VLA,因为例);
  4. 数组可以调整大小。