为链表编写前置的惯用方法是什么?

我开始在C中编写链表实现:

typedef struct node node;
struct node {
    node *next;
    int value;
};

我可以轻松编写将新节点附加到列表末尾的函数append:

void append(node *head, int value) {
    if (! head->next) {
        head->next = malloc(sizeof(node));
        head->next->value = value;
        return;
    }

    append(head->next, value);
}

如果我使用像Scheme这样的函数式语言编写前置,那么显而易见的事情就是简单地返回一个新节点,其中“next”指向前一个头:

(define (prepend head value)
        (cons value head))

我可以在C中轻松写出这个:

node *prepend(node *old_head, int value) {
    node* head = malloc(sizeof(node));
    head->value = value;
    head->next = old_head;

    return head;
}

但是现在我的append函数没有返回任何东西,只是改变了列表,而我的prepend函数确实返回了一些东西并且没有改变原始列表.这是链接列表实现方式的副作用,但感觉不对.

一种解决方案可能是重写prepend以添加新节点,但使用前一个头来获取新值,将新节点用于旧头的值…

void prepend(node *head, int value) {
    node* new = malloc(sizeof(node));
    memcpy(new, head, sizeof(node));

    head->next = new;
    head->value = value;
}

但是这件事也有些不对劲.

另一种解决方案可能是将列表表示为不作为头节点,而是作为指向头节点的不同结构:

typedef struct list list;
struct list {
    node *head;
};

现在我的prepend函数可以改变list-> head points的位置,而且它不必返回任何东西.这感觉最干净,但它引入了额外的行李;现在我必须使用辅助函数来追加和许多其他函数(或者以不同的方式实现它们).

在C中实现此功能的惯用方法是什么?

注意:我是C的新手,我很感激有关我的代码风格或正确性的任何评论,即使与问题无关.

最佳答案 在我看来,没有“正确”的答案,但我喜欢结构列表方法.您甚至可以使其对最终用户不透明.然后,您可以将底层实现更改为双向链接列表或由数组支持,并且用户无需更改任何代码即可利用它.

点赞