void addNewNode (struct node *head, int n)
{
struct node* temp = (struct node*) malloc(sizeof(struct node));
temp -> data = n;
temp -> link = head;
head = temp;
}
上面给出的代码是用于在链表头部添加新节点的功能的普遍错误版本。 通常正确的版本是,
void addNewNode (struct node **head, int n);
void addNewNode (struct node * &head, int n);
我为此目的制定了另一个但功能很简单的功能。
struct node* addNewNode (struct node *head, int n)
{
struct node* temp = (struct node*) malloc(sizeof(struct node));
temp -> data = n;
temp -> link = head;
return temp;
}
但我没有看到在代码和教程中使用或讨论过这个问题,因此我很想知道这种方法是否有一些缺陷。
答案 0 :(得分:17)
缺陷是你依靠调用者来执行更新指向列表的头指针的最后一步。
如果调用者忽略了这一点,编译器就不会抱怨,并且出于所有意图和目的,列表似乎没有改变(并且你已经泄漏了节点的内存)。
答案 1 :(得分:4)
链接列表在大多数功能语言中的工作方式。例如,在ML中你可能会这样做:
val ls = [1, 2, 3, 4]
val newList = 0 :: ls
::
语法实际上是一个函数,它接受两个参数(0
和ls
)并返回一个以0
为第一个元素的新列表。 ML中的列表实际上被定义为列表节点,因此::
实际上与您提议的addNewNode
函数非常相似。
换句话说:祝贺,您已经在C中创建了一个不可变的链表实现!理解这一点实际上是功能语言的一个相当重要的第一步,所以知道它真的是一件好事。
答案 2 :(得分:2)
您的方法与addNode
是列表中的方法的想法不相容,更常用于OO语言。
我个人认为
list.add(element)
比
更直观list = add(list, element)
数十个“集合”库不可错......
答案 3 :(得分:1)
Afaik,这就是glib中列表的工作方式,我确信gtk人不是第一个使用这种方式的人,所以我不会称之为新方法。我个人更喜欢有一个基本结构,它包含节点数,第一个和最后一个指针。
答案 4 :(得分:1)
这不是新的。正如quinmars所指出的那样,glib已经这样做了10多年。这是一个好主意,所以祝贺你搞清楚。
关于您的代码的一个挑剔:不要在C中强制转换malloc()
,并且不要在使用sizeof
时重复类型名称。您的分配行应如下所示:
struct node* temp = malloc(sizeof *temp);
请参阅?更短,更紧,更容易阅读,更难搞乱。更好! :)
答案 5 :(得分:0)
我发现任何提到的正确代码都没有问题。 更改或不更改头部是设计问题 - 以及如何返回修改后的列表。 良好的接口在std :: list<>中实现作为使用OOP的示例,这种方法没有潜在的问题。头指针是隐藏的,你可以根据需要修改它,并且由于调用者没有显式存储头,它对列表的引用总是正确的。
一件看起来很难看的东西(当你使用C ++而不是C时)是一个malloc,最好使用'new'。