为什么我们需要链接列表的C实现中的指针?

时间:2013-07-16 12:31:30

标签: c pointers linked-list

为什么在C?中实现链表的实现中使用指针很重要?

例如:

typedef struct item                                     
{
    type data;
    struct item *next;
} Item;

typedef struct list                                          
    Item *head;
} List;

如果Ill在没有指针的情况下使用相同的实现会发生什么?

7 个答案:

答案 0 :(得分:13)

嗯,你最终会得到像这样的东西

typedef struct item                                     
{
    type data;
    struct item next;
} Item;

现在C编译器将试图找出Item的大小。但由于next嵌入在Item中,因此最终会得到这样的等式

size-of-Item = size-of-type + size-of-Item

这是无限的。因此我们遇到了问题。因此,C需要指针,所以你有

size-of-Item = size-of-type + size-of-pointer

已关闭。更有趣的是,即使你在Java,Python或Haskell等语言中这样做,你也会隐含地存储一个指针(他们说引用)来打破循环。他们只是向你隐瞒事实。

答案 1 :(得分:8)

您实际上并不需要使用指针来实现公开链接列表接口的数据结构,但需要它们才能提供性能链表的特征。

指针的替代方案是什么?那么,item需要有一些方法来引用下一个项目。由于各种原因,它无法聚合下一个项目,其中一个原因是这会使结构“递归”,因此无法实现:

// does not compile -- an item has an item has an item has an item has...
typedef struct item                                     
{
    type data;
    struct item next;
} Item;

可以做的是拥有一系列项目并在其上实现链接列表:

Item *storage[100];

typedef struct item                                     
{
    type data;
    int next; // points to the index inside storage of the next item
} Item;

看起来它会起作用;你可以有一个将项目添加到列表中的函数和另一个删除它们的函数:

void add_after(Item *new, Item *existing);
void remove(Item *existing);

这些函数会从数组中删除existing(注意更新前一项的next“指针”),创建一个空槽,或找到existing项和在new的空白插槽中插入storage(更新next指向那里)。

这样做的问题在于,这使得add_afterremove操作无法在恒定时间内实现,因为您现在需要搜索数组并在空间不足时重新分配它。

由于常量时间添加/删除是人们使用链表的 原因,这使得非指针方法仅作为智力练习有用。对于真实列表,使用指针意味着您可以在固定时间内执行这些操作。

答案 2 :(得分:4)

如果没有指针,每个列表项都包含另一个列表项(next),其中包含另一个列表项。其中又包含另一个列表项。等等。

你最终会得到一个无限大的数据结构,它会使用无限量的内存,当然这是无法工作的。

答案 3 :(得分:4)

指针用于动态分配内存。

您可以将列表实现为一个简单的数组,但这意味着您要分配一个连续的内存区域,这对于大型列表来说效率不高。此外,数组更难以扩展(如果达到其最大大小,则必须重新分配它)。

因此,对于大型列表,动态分配是首选,对于每个元素,它可以在任何地方,无论是否连续地将它分配到内存中,并且您可以轻松地扩展它。

此外,您无法存储在struct item另一个struct item中,因为该结构尚未定义且无法确定结构的大小:

无法完成

typedef struct item                                     
{
    type data;
    struct item next;
} Item;

因此使用指针是因为在编译结构时可以确定指针的大小(该平台上的整数的大小)。

答案 4 :(得分:3)

您不需要指针来在C中创建列表。如果指针列表更可取,取决于您的应用程序。人们在使用语言实现指针概念之前使用了链表。对于某些人(使用指针)来说,有时候只能理解它。您不需要具有数据的结构。

一个简单的数组就可以了。你需要:

  • 一个用于保存索引的整数数组(这类似于指针)。数组内容为-1(在指针术语中表示nilNULL),或0 .. N-1表示:链接的下一个元素的数组索引。 索引数组的索引表示数据数组中项的索引。
  • 一个数组,用于在上面的“列表”中“连接”任何数据。

这就是你所需要的一切。

数组而不是指针列表可能有

  • 优势,如果您的数据量没有变化(不会增长)。在这种情况下,优势可能是巨大的。如果您知道您将遇到的最大数据并且可以事先分配数据,那么数组实现将比任何指针链接列表快得多。特别是,如果您只插入,但不需要删除。
  • 缺点否则。在这种情况下,缺点可能很大。

请阅读here

因此,您的示例将如下所示:

type data[N];  // or: type *data = new type [N];
int links[N];  // or:  int *links = new int [N];

这应该是从一个简单的测试用例开始所需的全部内容。

答案 5 :(得分:1)

c

中指针的重要性

的优点: -

  1. 执行速度会很快。

  2. 指针允许我们访问函数之外的变量。

  3. 减少代码的大小。

  4. 没有指针: -

    1. 代码大小会增加。

    2. 我们无法有效维护数据。

    3. 在列表中,指针用于指向列表中的下一个项目。如果未声明指针,则表示您无法从列表中访问多个项目。

    4. 如果你在运行时动态分配一些内存,那么指针就非常需要。

答案 6 :(得分:0)

有不止一种“清单”。您显示的实现是“链接列表”。没有指针就没有“链接”,所以我们需要查看其他类型的列表。

所以,首先,如果你只是从结构定义中删除*会发生什么:它不会编译。你可以在另一个结构中包含一个结构,但是如果有递归那么结构将具有不会发生的无限大小!

如何将数组作为列表:

struct item list[100];

是的,这会奏效,但现在你的清单是固定的。

好的,让我们动态分配列表:

struct item *list = malloc(sizeof(struct item));

然后,每次添加到列表时,您都必须执行此操作:

list = realloc(list, newcount * sizeof(struct item));
list[newitem] = blah;

好的,这有效,但现在我们经常重新分配内存,导致大量内存副本和效率低下。

另一方面,如果列表很少更新,这样更节省空间,这可能是一件好事。

使用数组的另一个缺点是诸如推,弹,排序,反转等操作变得更加昂贵;它们都意味着多个内存副本。

还有其他类型的“列表”,但它们都涉及指针,所以我想我们可以在这里忽略它们。