【问题标题】:Memory Space Usage: Linked List Pointers vs Array Indexes内存空间使用:链表指针与数组索引
【发布时间】:2017-05-20 23:23:53
【问题描述】:

我正在学习链表,我很好奇与具有相同元素的数组相比,链表需要多少内存空间。 This page 列出了以下链表的缺点:

列表的每个元素都需要额外的内存空间来存放指针。

但是数组也必须为每个元素的索引使用内存,对吧?

我猜索引会比指针占用更少的内存,但我很好奇我们会谈论什么样的比率。有什么想法吗?

【问题讨论】:

  • 这取决于你的语言——最纯粹形式的数组不需要使用内存来存储索引(即使使用混合数组也可以抽象出索引)——如果你知道有多少内存每个元素都可以通过简单地计算n * element_size,轻松计算出内存中索引n处的元素。

标签: arrays pointers indexing data-structures linked-list


【解决方案1】:

如果您使用 value-types 作为数组元素,那么数组使用的存储空间更少。

我以 C 为例:

1。值数组:

struct Value {
    int x;
}

sizeof(Value) == 4 bytes (assuming zero overhead)

struct Value staticArray[10]; // will consume 10 * 4 == 40 bytes

2。如果是指针数组,那么:

struct Value* staticArrayOfPointers[10];
for( size_t i = 0; i < sizeof(staticArrayOfPointers); i++ ) {
    staticArrayOfPointers[i] = calloc( sizeof(struct Value), 1 );
}
  • staticArrayOfPointers 本身无论如何都会消耗 40 个字节,因为指针是 4 个字节长
  • 但它还需要为每个索引分别分配每个元素,因此还有 40 个字节。
  • 总共 80 个字节。

3。如果是使用内联值的链表,则使用堆:

struct Node {
    struct Value value; // stored inline, as a value, in the node, not elsewhere in memory
    Node* next;
}

sizeof(struct Node) == 8 bytes (assuming zero overhead and alignment, and 4-byte pointers)

struct Node* head = calloc( sizeof(struct Node), 1 ); // calloc zeroes memory
struct Node* current = head;
for( size_t i = 0; i < 10; i++ ) {
    current->next = calloc( sizeof(struct Node), 1 );
    current = current->next;
}

现在,链表在内存中至少有 10 * 8 (80) 个字节,并且也可能是碎片化的,因为每个节点可能存在于内存中的不同位置。

4。如果它是使用指向值的指针的链表(也使用堆):

struct Node2 {
    struct Value* valuePtr;
    Node* next;
}

sizeof(struct Node2) == 8 (as a pointer is also 4 bytes), but then add another 4 bytes for the `Value` instance located elsewhere:

struct Node2* head = calloc( sizeof(struct Node2), 1 ); // calloc zeroes memory
head->valuePtr = calloc( sizeof(struct Value), 1 );
struct Node2* current = head;
for( size_t i = 0; i < 10; i++ ) {
    current->next = calloc( sizeof(struct Node2), 1 );
    current->valuePtr = calloc( sizeof(struct Value), 1 );
    current = current->next;
}

此版本现在将消耗 10 * ( 8 + 4 ) == 120 字节,但随着分配的增加,碎片的可能性也增加了。

总结:

  • 值数组:n * sizeof(Element)
  • 指向值的指针数组:n * ( sizeof(Element*) + sizeof(Element) )
  • 值的链接列表:n * ( sizeof(Element) + sizeof(Node*) )
  • 指向值的指针的链接列表:n * ( sizeof(Element*) + sizeof(Element) + sizeof(Node*) )

注意事项:

  • 这取决于语言。如果它是一种像 C 或 C++ 这样可以让您完全控制内存的语言,那么您可以使用上述任何一种方法。
  • 如果是Java,那么只有原始类型(intdouble,但不是string)是值类型,所有其他类型都是“引用类型”并在内部使用指针,但因为它是一个GC环境中您不会遭受致命的内存碎片...希望如此。
  • 如果是 C# 或 Swift,您可以使用值类型(structclass),那么您可以使用所有 4 个选项,但指针本身不会暴露。
  • JavaScript 将内部表示抽象掉 - 所以您认为的 Number 值的简单数组在内部可能是哈希映射或向量(尽管它的行为相同)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-19
    • 2012-07-11
    • 1970-01-01
    • 1970-01-01
    • 2020-10-11
    • 2011-04-17
    相关资源
    最近更新 更多