为什么在链表的C实现中需要指针?

为什么在链表的C实现中需要指针?,c,pointers,linked-list,C,Pointers,Linked List,为什么在C中链表的实现中使用指针很重要 例如: typedef struct item { type data; struct item *next; } Item; typedef struct list Item *head; } List; 如果我只使用没有指针的相同实现,会发生什么情况?如果没有指针,每个

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

例如:

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

typedef struct list                                          
    Item *head;
} List;

如果我只使用没有指针的相同实现,会发生什么情况?

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


您最终会得到一个无限大的数据结构,该结构将使用无限大的内存,而这当然无法工作。

您最终会得到类似的结果

typedef struct item                                     
{
    type data;
    struct item next;
} Item;
size-of-Item = size-of-type + size-of-Item
现在,C编译器将尝试计算
项的大小。但是由于
next
正好嵌入到
项中,因此它将以这样一个等式结束

typedef struct item                                     
{
    type data;
    struct 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等语言中这样做,您实际上是在隐式存储一个指针(他们称之为引用)来打破循环。它们只是对您隐藏事实。

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

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

// 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);
这些函数要么从数组中删除
现有的
(注意更新上一项的
下一个
“指针”),创建一个空槽,要么找到
现有的
项,并将
新的
插入
存储器的空槽中(更新
下一个
指向那里)

问题在于,这使得
add\u after
remove
操作无法在固定时间内实现,因为现在需要搜索数组并在空间不足时重新分配它


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

指针用于动态分配内存

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

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

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

这是无法做到的

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

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

c中指针的重要性

优点:-

  • 执行速度将很高

  • 指针允许我们访问函数外部的变量

  • 减少代码的大小

  • 没有指针:-

  • 代码大小将得到增加

  • 我们无法有效地维护数据

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

  • 如果您在运行时动态分配一些内存,那么实际上非常需要指针


  • 在C中创建列表不需要指针。指针列表是否可取取决于您的应用程序。在用语言实现指针概念之前,人们使用链表。对某些人来说(使用指针)有时理解起来更简单。您也不需要包含数据的结构

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

    • 一个整数数组,用于保存索引(类似于指针)。数组内容可以是
      -1
      (指针术语中的
      nil
      NULL
      ),也可以是
      0。。N-1
      ,表示:链接的下一个元素的数组索引。索引数组的索引表示数据数组中项的索引
    • 一个数组,用于上面“列表”中要“连接”的任何数据
    这就是你所需要的

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

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

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

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

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

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

    因此,首先,如果从结构定义中删除
    *
    ,会发生什么:它不会编译。Y
    list = realloc(list, newcount * sizeof(struct item));
    list[newitem] = blah;