在C中为单链表API设计函数原型

时间:2013-05-19 01:38:20

标签: c api linked-list function-prototypes

我正在用C语言重写我几年前学到的所有数据结构,以增加对数据结构和C语言的理解。我正在做的第一个是单链接列表,所以我正在为它们设计一个API。我还没有编写任何实现,但我希望得到一些关于我的函数原型的反馈,以确保我所做的事情是明智和合乎逻辑的。

首先是一些评论:

  1. 我希望很多人会为节点结构建议一个typedef,但这是个人决定,我不想输入结构。

  2. 我最不确定我对返回类型和值的选择。我有两个函数返回指向已删除节点的数据变量的指针。这些函数从列表中删除节点,保存指向数据的指针,并释放节点。这意味着用户需要释放返回的数据。这是不好的形式?我不确定如何以允许静态返回数据的方式来执行此操作,因为这需要提前知道数据的大小,这会削弱库的有用性。

  3. 我想让API支持所有可以通过单链表实现的有用的东西,其中一个是实现堆栈。在单链表上推送/弹出操作困扰我的一件事是,似乎有些人头脑中的概念是推/弹应该只发生在列表的 end 。如果您需要在尾部(结束)执行推/弹操作,则堆栈作为后进/先出机制(LIFO)不适用于单链接列表。这显然是因为使用尾部而不是列表的头部效率非常低,因为只能通过遍历列表来到达最后一个元素。下面的push / pop函数模仿堆栈的行为,但操作发生在列表的前面。这是一个糟糕的设计吗?

  4. 以下是包含元素的列表示例:

    [SENTINEL] - GT; [0] - > [1] - > [2] - > [3] - > NULL

    每个节点都是struct node类型。 struct node是一个带有数据和下一个字段的结构:

    struct node {
        void *data;
        struct node *next;
    }
    

    此实现将使用sentinel节点来简化边界条件。每个列表都有一个标记节点,位于列表的前面。

    空列表包含一个指向null的sentinel节点,如下所示: [SENTINEL] - GT; NULL

    列表的结构为struct sllist。 struct sllist有一个sentinel字段,其中包含指向sentinel节点的指针:

    struct sllist {
        struct node *sentinel;
    }
    

    最后,这是一个操作列表及其相关的函数原型(带描述):

        //create new list:
        struct *sllist new_sllist();
                //Returns a pointer to a new, empty sllist.
    
        //destroy list:
        void destroy_sllist(struct sllist *sllist); 
                //Frees the memory of the list struct and all associated nodes.
    
        //insert after node:    
        int sllist_insert_after(struct sllist *sllist, struct *node, void *data); 
                //Adds a node after the passed node.
                //If allocation fails, returns -1, otherwise returns 0.
    
        //prepend node to the list:
        int sllist_push(struct sllist *sllist, void *data);
                //Adds a node to the front of the list. If allocation fails, returns -1, otherwise returns 0.
                //Note that the front of the list is defined to be the first node after the sentinel node.
    
        //extract after node:   
        void* sllist_extract_after(struct sllist *sllist, struct node *node);
                //Removes a node from the linked list, save a pointer to the data, free the node 
                //(but do not the data itself), and return a pointer to the data so that it can be used. 
                //If the node doesn't exist in the list, returns NULL.
    
        //extract first node:
        void* sllist_pop(struct sllist *sllist);
                //Same as extract after node, but restricted to the first node (first node after sentinel.)
                //Analogous to sllist_push(), the name sllist_pop suggests usage as a stack.
    
        //destroy after node:
        void sllist_destroy_after(struct sllist *sllist, struct node *node);
                //Removes from the list the node after the passed node and frees all associated memory.
    

    如果有什么东西看起来不合时宜,很奇怪或设计不合理,请告诉我。

1 个答案:

答案 0 :(得分:0)

链表通常是更高级数据结构的实现细节,即队列,列表,堆栈等......所以,我确保实现每个所需的所有操作。 push_backpush_frontpop_frontpop_backfrontback,当然还有随机访问权限。

对于有效负载,用户应负责分配和解除分配。毕竟,他们可能正在向堆栈上的内存传递一个指针,你当然不希望释放那个内存。另一种方法是在插入时制作有效载荷的副本(即memcpy),然后你可以确保你的内存在弹出时释放,并且你不会对用户造成任何意外行为:这是以牺牲但是数据的线性副本。

此外,您可能会发现将链接列表修改为以下内容很有帮助:

struct sllist {
struct node *head;
struct node *tail;
struct node *current;
unsigned size;
}