在 C 中创建和理解结构的链表 [英] Creating and Understanding linked lists of structs in C

查看:25
本文介绍了在 C 中创建和理解结构的链表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我无法同时掌握struct 和链表数据结构的概念.例如,假设我们有这样的代码:一个 struct ,它包含一个 worker 的内容和一个这些结构的链表,其中包含每个 worker 的节点和一个指向下一个节点(?)的指针.

I am having trouble grasping the concepts of struct and the linked list data structure together. For example lets say we have have this code: a struct that has a worker's content and a linked list of these structs that contains nodes of each worker and a pointer to the next node(?).

    typedef struct Schedule {
        char name[10];
        char description[10];
        int hours;
        int workordernum;
    } Work;

    typedef struct linkedlist {
        struct Schedule work;
        struct linkedlist *next;
    } Node;

问题是你如何制作一个总是在列表开头添加一个节点的方法,一个使用用户定义的workordernum在列表中的任何地方(中间)添加它的方法,以及一个总是把它放在最后的方法.

The problem is how do you make a method that always adds a node in the beginning of the list, a method that adds it anywhere(middle) in the list using user defined workordernum, and a method that always puts it in the end.

我不太理解 ->* 的正确用法.我确实在网上阅读了有关创建头和尾节点的信息,但我并没有完全正确地使用它,因为它们有一个 struct 用于列表和一个 struct 用于节点.

I am not quite understanding -> and * usages properly. I did read online about creating head and tail nodes but I didn't quite get its usage properly, since they had a struct for a list and a struct for a node.

我没有得到的另一件事是,假设在列表的开头添加一个节点有效,那么您如何更改所有节点的每个 workordernum 值以前在那里?

Another thing which I didn't get is, lets say adding a node to the beginning of the list works, how do you go about then changing every single workordernum value for all the nodes that were previously there?

我知道每次添加、删除或移动节点时都必须跟踪,这意味着每次调用这些方法时,我们都必须有一个变量来跟踪数字.那么如果链表中的一个节点都准备好​​了,那么它的顺序就是一个,那么我们在开头再添加一个,我们如何将顺序号1变为2,添加到1的顺序呢?

I understand one must keep track of every time a node gets added, removed, or moved, meaning every time these methods are called we must have a variable that keeps track of the number. So if we have a node in the list all ready, its order would be one, then we add another one to the beginning, how would we change the order number 1 to 2 and the one being added to 1?

或者如果我们只有一个指针,node->next->next->next 是如何工作的?那么我们如何打印所有这些呢?因为我们不能使用 for 循环.

Or how does node->next->next->next work if we only have one pointer? Then how would we print all of them? Since we cannot use a for loop.

所以这些是我无法掌握代码的概念.如果您能花时间解释它而不是仅仅提供代码,我将不胜感激.因为我还必须将我学到的知识应用于移动和删除节点.我想自学.如果必须将某些内容作为代码示例给出,那很好,但请不要为我发布所有答案代码.

So these are the concepts I cant grasp code wise. I would KINDLY appreciate it if you take your time to explain it rather than just give the code, if possible. Because I would have to apply what I learn to moving around and deleting nodes as well. I want to learn it on my own. If something must be given as a code example then that's fine but please just don't post all the answer codes for me.

-谢谢

*请原谅任何格式错误,因为我是这个网站的新手.

*Please forgive any format errors since I am new to this site.

我确实理解指针是一个地址,并且 -> 与指向"成员有关.我的意思是我理解所有基础知识,但我的理解不够坚定,否则我可以做我需要帮助的事情.

I do understand a pointer is an address and that -> pertains to "pointing to" a member. I mean I understand all the basics, but my understanding isn't firm enough else I could be doing what I need help with.

Edit2:我将尝试根据我目前学到的知识制作一个带有链表的头节点.我将使用上面的结构,这将是松散的代码,并不完美.这只是为了确保我目前走在正确的轨道上.

I will try and make a head node with linked list from what I have learned so far. I will be using the structs above and it will be loose code, not perfect. This is just to make sure I'm on the right track so far.

int main() {

   // creates a work struct to hold user content
   Work *workstruct = (Work*)malloc((sizeof(struct Schedule));

   // creates first node to hold a work struct for linkedlist
   Node *initialNode = (Node*)malloc((sizeof(struct linkedlist));

   // Method call to add work nodes to list in main
   addWork(initialNode, workstruct);

}

void addWork(Node *initialNode, Work *workstruct) {

   // Suppose user already initialized workstruct

   // create double-pointer to make initialNode as head
   Node **head = (Node **)malloc(sizeof(struct linkedlist));

   // assigns user's workstruct to the workstruct of initialNode
   initialNode->work = *workstruct;

   // If im understanding what you have taught me so far,
   // this is how you always add a node on the head

   initialNode->next = *head;
   *head = initialNode;
}

到目前为止,我似乎遇到的唯一问题是,每次我尝试向列表中添加一个新节点时,它都会使新节点成为头部,但会丢失列表中的前一个节点.

The only issue I seem to have so far is that every time I try and add a new node to the list, it makes the new node the head but loses the previous node that was in the list.

推荐答案

Linked Lists - 101 - Singly-linked Lists

这是一个很长的答案.我之所以说得如此详细,是因为我希望在适当的上下文中就地回答大量链表问题.

当我学习 C 时,我很难使用指针.不过,在实现了一个链表之后,我终于开始掌握指针的概念.主链表在 C 中是一件好事,它将帮助您熟悉指针.当事情看起来很混乱时,拿起铅笔和纸,画出一个列表图和到节点的相关链接.当我处理复杂的列表实现时,我偶尔会这样做.

When I learned C, I had a hard time with pointers. However, after implementing a linked list, I finally started to grasp the concept of pointers. Master linked lists is a good thing in C, and it will help you be comfortable with pointers. When things seem confusing, grab a pencil and paper and sketch out a diagram of a list and the associated links to nodes. I occasionally do that when I am working with complex list implementations.

链表是一种存储数据记录的方式.与所有元素都占用一个连续内存块的数组不同,链表元素占用内存的随机片段.

A linked list is a way to store data records. Unlike an array where all elements occupy one contiguous memory block of memory, linked list elements occupy random fragments of memory.

链表有两种基本类型;单链表和双链表.不同的是单向链表只能单向遍历;而双向链表可以双向遍历.

There are two basic types of linked list; a singly-linked list, and a doubly-linked list. The difference is that a singly-linked list can only be traversed in one direction; while a doubly-linked list can be traversed in both directions.

单向链表是通过它的头"指针或指向头链表节点的指针来访问的.双向链表也可以通过其头"指针或尾"指针访问.

A singly-linked list is accessed via its "head" pointer, or a pointer to the head list node. A doubly-linked list may also be accessed by via its "head" pointer, or via its "tail" pointer.

与数组的每个元素都可以通过其数组索引直接寻址的数组不同,链表元素是按顺序访问的.

Unlike an array where each element of the array can be directly addressed by its array index, linked list elements are accessed sequentially.

这是一个单向链表的布局:

Here is a layout of a singly-linked list:

            Node #1      Node #2      Node #3      EndOfList
            ----------   ----------   --------     ---------
HEADPTR-->  NEXTPTR-->   NEXTPTR-->   NEXTPTR-->   NULL
            DataPayload  DataPayload  DataPayload

列表中的每个节点及其数据负载都是单独分配的.节点结构(在 C 中)可能如下所示:

Each node in the list, with its data payload, is separately allocated. A node structure (in C) might look something like this:

    typedef struct NODE_PAYLOAD_S
       {
       /* Data Payload (defined by coder) */
       char name[10];
       char desc[10];
       int hours;
       int workordernum;
       } NODE_PAYLOAD_T;

    typedef struct LIST_NODE_S
       {
       /* Next-node pointer */
       struct LIST_NODE_S *next;     /* pointer to the next node in the list. */
       NODE_PAYLOAD_T      payload;  /* Data Payload (defined by coder) */
       } LIST_NODE_T;

初始化上述结构的单链表:

To initialize a singly-linked list of the above structure:

LIST_NODE_T *listHead = NULL;

'listHead' 现在是一个指向链表(没有节点)的指针.

'listHead' is now a pointer to a linked list (with no nodes).

这里是如何在这个列表的头部添加一个新节点:

Here is how to add a new node at the head of the this list:

int LIST_InsertHeadNode(
      LIST_NODE_T **IO_head,

<小时>

问:为什么这里需要双指针"(即:LIST_NODE_T **...)?为什么不使用单级"指针(即:LIST_NODE_T *...)?


Q: Why is a "double-pointer" required here (ie: LIST_NODE_T **...)? Why not use a "single-level' pointer (ie: LIST_NODE_T *...)?

A:指向列表头的单个"指针对于此操作是不够的.具体而言,该操作指定新的头节点".这意味着this函数需要修改指向头节点的指针.

A: A "single" pointer to the list head would not be sufficient for this operation. Specifically, this operation designates a new "head node". This means that the this function needs to modify the pointer that points to the head node.

之前:

                         Node #Y      Node #Z      EndOfList
                         ----------   ----------   ---------
             HEADPTR-->  NEXTPTR-->   NEXTPTR-->   NULL
                         DataPayload  DataPayload  

之后:

            New Node      Node #Y      Node #Z      EndOfList
            ----------   ----------   --------     ---------
HEADPTR-->  NEXTPTR-->   NEXTPTR-->   NEXTPTR-->   NULL
            DataPayload  DataPayload  DataPayload

请注意,之前 HEADPTR 指向的是节点 #Y";然后,HEADPTR 指向新节点".调用这个函数时,传入的是listHead指针的地址,允许这个函数改变listHead指针指向的位置.换句话说,listHead 指针的地址被传递给这个函数,它被(在这个函数内部)表示为一个指向 listHead 指针的指针(一个指向一个指针的指针).这就是为什么它是一个双指针".

Note that before, HEADPTR was pointing to 'Node #Y'; then after, HEADPTR is pointing at 'New node'. When this function is called, the address of the listHead pointer is passed in, allowing this function to change where the listHead pointer is pointing. In otherwords, the address of the listHead pointer is being passed into this function, which is represented (internally to this function) as a pointer to the listHead pointer (a pointer to a pointer). That is why it is a "double-pointer".

      char         *I__name,
      char         *I__desc,
      int           I__hours,
      int           I__workordernum
      )
   {
   int rCode=0;
   LIST_NODE_T *newNode = NULL;

   /* Allocate memory for new node (with its payload). */
   newNode=malloc(sizeof(*newNode));
   if(NULL == newNode)
      {
      rCode=ENOMEM;   /* ENOMEM is defined in errno.h */
      fprintf(stderr, "malloc() failed.
");
      goto CLEANUP;

<小时>

问:'goto CLEANUP;' 是什么意思?生意?


Q: What's this 'goto CLEANUP;' business?

A:C 语言与 C++ 和 JAVA 不同,没有异常"的概念.C 中的错误检查至关重要.malloc() 函数可能失败的原因有很多,如果失败,代码必须尽可能优雅地处理它.'goto CLEANUP' 语句使正常程序流跳过代码跳转到 'CLEANUP:' 标签(在此函数中,如下).

A: The C language, unlike C++ and JAVA, has no concept of an 'exception'. Error checking in C is critical. There are a number of reasons that the malloc() function might fail, and if it does, the code has to handle it as gracefully as possible. The 'goto CLEANUP' statement causes the normal program flow to skip code jumping to the 'CLEANUP:' label (within this function, below).

显然,如果 malloc() 失败,尝试用紧接其后的行初始化 NULL 指针(由失败的 malloc 返回)是不明智的.所以,重要的是转移程序的流程以跳过这个初始化(以及后面的链接).

Obviously, if malloc() has failed, it would be unwise to try to initialize the NULL pointer (returned by the failed malloc) with the lines that immediately follow. So, it is important to divert the flow of the program to skip this initialization (and the linkage that comes later).

CLEANUP:"标签没有什么特别之处.我可以称它为ERROR:"、EXIT:"、FINISH:"、LIAHONA:"、MY_BAD"或其他任何我喜欢的东西.(标签不一定要大写,也不一定要放在左边距.不过,我的风格是这样做以使其脱颖而出.)

There is nothing special about the 'CLEANUP:' label. I could have called it 'ERROR:', 'EXIT:', 'FINISH:', 'LIAHONA:', 'MY_BAD' or anything else that suited my pleasure. (Labels don't have to be uppercase, nor do they have to be placed at the left margin. However, my style is to do that so that they stand out.)

标签,例如CLEANUP:",其作用域仅限于放置它们的函数的边界;这允许每个函数都有一个唯一的CLEANUP:"标签(如果需要).

Labels, such as 'CLEANUP:' has a scope that is limited to the boundaries of the function where they are placed; which allows each function to have a unique 'CLEANUP:' label (if needed).

      }

   /* Initialize the new node's payload. */       
   snprintf(newNode->payload.name, sizeof(newNode->payload.name), "%s", I__name);
   snprintf(newNode->payload.desc, sizeof(newNode->payload.desc), "%s", I__desc);
   newNode->payload.hours = I__hours;
   newNode->payload.workordernum = I__workordernum;

   /* Link this node into the list as the new head node. */
   newNode->next = *IO_head;
   *IO_head = newNode;

CLEANUP:

   return(rCode);
   }

上述函数可能被调用如下:

The above function might be called as follows:

#include <stdio.h>
#include <errno.h>

int LIST_InsertHeadNode(LIST_NODE_T **, char *, char *, int, int);

int main(void)
   {
   int rCode=0;
   LIST_NODE_T *listHead = NULL;

   rCode=LIST_InsertHeadNode(&listHead, "Mahonri", "Jareds Bro", 4, 2421);
   if(rCode)
      {
      fprintf(stderr, "LIST_InsertHeadNode() reports: %d
", rCode);
      goto CLEANUP;
      }

CLEANUP:

   return(rCode);
   }

LIST_InsertHeadNode() 函数可能会被多次调用.每次调用都会向列表中添加一个新节点.新节点将被放置在链表的头部",这具有将其余节点推到链表下方的效果.

The LIST_InsertHeadNode() function may be called multiple times. Each call will add a new node to the list. The new node will be placed at the "head" of the list, which has the effect of pushing the rest of the nodes further down the list.

在列表中添加几个节点后,访问列表可能会很好;也许打印每个节点的有效载荷:

After adding several nodes to the list, it might be good to access the list; perhaps to print the payload of each node:

int PrintListPayloads(
      LIST_NODE_T *head;
      )
   {
   int rCode=0;
   LIST_NODE_T *cur = head
   int nodeCnt=0;

   while(cur)
      {
      ++nodeCnt;
      printf("%s, %s, %d, %d
",
            cur->payload.name,
            cur->payload.desc,
            cur->payload.hours,
            cur->payload.workordernum
            );
       cur=cur->next;
       }

    printf("%d nodes printed.
", nodeCnt);

   return(rCode);
   }

上面的函数可以从main()调用:

The above function can be called from main():

#include <stdio.h>
#include <errno.h>

int LIST_InsertHeadNode(LIST_NODE_T **, char *, char *, int, int);
int PrintListPayloads(LIST_NODE_T *);

int main(void)
   {
   int rCode=0;
   LIST_NODE_T *listHead = NULL;

   /* Insert a linked-list node. */
   rCode=LIST_InsertHeadNode(&listHead, "Mahonri", "Jareds Bro", 4, 2421);
   if(rCode)
      {
      fprintf(stderr, "LIST_InsertHeadNode() reports: %d
", rCode);
      goto CLEANUP;
      }

   /* Insert a linked-list node. */
   rCode=LIST_InsertHeadNode(&listHead, "Joe", "CEO", 5, 2419);
   if(rCode)
      {
      fprintf(stderr, "LIST_InsertHeadNode() reports: %d
", rCode);
      goto CLEANUP;
      }

   /* Insert a linked-list node. */
   rCode=LIST_InsertHeadNode(&listHead, "Eve", "Mother", 24, 2);
   if(rCode)
      {
      fprintf(stderr, "LIST_InsertHeadNode() reports: %d
", rCode);
      goto CLEANUP;
      }

   rCode=PrintListPayloads(listHerad);
   if(rCode)
      {
      fprintf(stderr, "PrintListPayloads() reports: %d
", rCode);
      goto CLEANUP;
      }

CLEANUP:

   return(rCode);
   }

在列表的头部添加节点[即:LIST_InsertHeadNode()]是添加节点的一种方式.但是,有时将节点添加到列表的另一端(即:列表尾部")是更可取的.下面的代码显示了这是如何完成的.

Adding nodes at the head of a list [ie: LIST_InsertHeadNode()] is one way to add nodes. However, there are times where adding nodes to the other end of the list (ie: the list 'tail') is preferable. The code below shows how this is done.

首先,一个函数将返回列表的当前尾节点".

First, a function that will return the current 'tail node' of a list.

   int LIST_GetTailNode(
         LIST_NODE_T  *I__listHead,   /* The caller supplied list head pointer. */
         LIST_NODE_T **_O_listTail    /* The function sets the callers pointer to the
                                         last node. */
         )
      {
      int rCode=0;
      LIST_NODE_T *curNode = I__listHead;

      /* Iterate through all list nodes until the last node is found. */
      /* The last node's 'next' field, which is always NULL. */
      if(curNode)
         {
         while(curNode->next)
            curNode=curNode->next;
         }

      /* Set the caller's pointer to point to the last (ie: tail) node. */
      if(_O_listTail)
         *_O_listTail = curNode;

      return(rCode);
      }

接下来,将在列表尾部插入一个节点的函数.

Next, a function that will insert a node at the tail of the list.

   int LIST_InsertTailNode(
      LIST_NODE_T **IO_head,
      char         *I__name,
      char         *I__desc,
      int           I__hours,
      int           I__workordernum
      )
   {
   int rCode=0;
   LIST_NODE_T *tailNode;
   LIST_NODE_T *newNode = NULL;

   /* Get a pointer to the last node in the list. */
   rCode=LIST_GetTailNode(*IO_head, &tailNode);
   if(rCode)
      {
      fprintf(stderr, "LIST_GetTailNode() reports: %d
", rCode);
      goto CLEANUP;
      }

<小时>

重要说明:LIST_GetTailNode() 函数会将tailNode 指针设置为链表中的最后一个节点;- 除非 - 列表中没有节点.当列表为空时,LIST_GetTailNode() 会将tailNode 指针设置为NULL.


Important note: The LIST_GetTailNode() function will set the tailNode pointer to the last node in the linked list; -unless- there are no nodes in the list. When the list is empty, LIST_GetTailNode() will set the tailNode pointer to NULL.

   /* Allocate memory for new node (with its payload). */
   newNode=malloc(sizeof(*newNode));
   if(NULL == newNode)
      {
      rCode=ENOMEM;   /* ENOMEM is defined in errno.h */
      fprintf(stderr, "malloc() failed.
");
      goto CLEANUP;
      }

   /* Initialize the new node's payload. */       
   snprintf(newNode->payload.name, sizeof(newNode->payload.name), "%s", I__name);
   snprintf(newNode->payload.desc, sizeof(newNode->payload.desc), "%s", I__desc);
   newNode->payload.hours = I__hours;
   newNode->payload.workordernum = I__workordernum;

   /* Link this node into the list as the new tail node. */
   newNode->next = NULL;
   if(tailNode)
      tailNode->next = newNode;
   else

这个'else'情况表示当tailNode为NULL时发生,这意味着(当前)链表没有节点.在这种情况下,此节点将是列表中的第一个(头)节点(以及最后一个).因此,调用者的List Head"指针被更新以指示这个新节点现在是头节点.

This 'else' case indicates occurs when tailNode is NULL, meaning that (currently) the linked list has no nodes. In this case, this node will be the first (head) node in the list (as well as the last). So, the caller's 'List Head' pointer is updated to indicate that this new node is now the head node.

      *IO_head = newNode;

CLEANUP:

   return(rCode);
   }

LIST_InsertTailNode() 函数的调用方式与 LIST_InsertHeadNode() 的调用方式相同.唯一的区别是使用 LIST_InsertTailNode(),新节点插入到列表的尾部,而不是列表的头部.

The LIST_InsertTailNode() function is called the same way LIST_InsertHeadNode() is called. The only difference is that with LIST_InsertTailNode(), the new node is inserted at the list's tail, rather than at the list's head.

好的,现在您可以在列表的头部或尾部插入一个新节点.在列表中间的某处插入一个新节点怎么样?

OK, so now you can insert a new node at the head, or tail of the list. What about inserting a new node somewhere in the middle of the list?

例如,假设您想要一个列表,其中所有节点都按有效负载中的某个字段(如名称")进行排序.虽然可以添加所有节点,然后对列表后记进行排序;将每个新节点插入到列表的适当位置要容易得多.这样做,列表将始终自动按排序顺序维护.

For example, assume that you want to have a list where all the nodes are sorted by some field in the payload like 'name'. While it is possible to add all the nodes, and then sort the list afterwords; it is much easier to insert each new node into the list in it's proper place. Doing that, the list will always be maintained in sorted order automatically.

这分两步完成.首先,分配并初始化新节点.然后找出它在列表中的正确位置,然后将新节点链接到该位置的列表中.

Accomplishing this is done in two steps. First, allocate and initialize the new node. Then figure out where its proper place is in the list, then link the new node into the list at that location.

首先,一个函数会将父节点"返回给新节点.(这个节点假设列表是按名称排序的):

First, a function that will return what will be the 'parent node' to the new node. (This node assumes that the list is being maintained in sorted order by name):

int LIST_FetchParentNodeByName( 
      LIST_NODE_T *I__head,
      const char  *I__name,
      LIST_NODE_T **_O_parent
      )
   {
   int rCode=0;
   LIST_NODE_T *parent = NULL;
   LIST_NODE_T *curNode = I__head;

   /* Inform the caller of an 'empty list' condition. */
   if(NULL == I__head)
      {
      rCode=ENOENT;
      goto CLEANUP;
      }

   /* Find a node with a payload->name string greater than the I__name string */
   while(curNode)
      {
      if(strcmp(curNode->payload.name, I__name) > 0)
         break;

      parent = curNode; /* Remember this node. It is the parent of the next node. */
      curNode=curNode->next;  /* On to the next node. */
      }

   /* Set the caller's 'parent' pointer. */
   if(_O_parent)
      *_O_parent = parent;

CLEANUP:

   return(rCode);
   }

现在,一个函数将插入新节点,保持列表按名称排序.

And now, a function that will insert new nodes, keeping the list sorted by name.

   int LIST_InsertNodeByName(
      LIST_NODE_T **IO_head,
      char         *I__name,
      char         *I__desc,
      int           I__hours,
      int           I__workordernum
      )
   {
   int rCode=0;
   LIST_NODE_T *parent;
   LIST_NODE_T *newNode = NULL;

   /* Allocate memory for new node (with its payload). */
   newNode=malloc(sizeof(*newNode));
   if(NULL == newNode)
      {
      rCode=ENOMEM;   /* ENOMEM is defined in errno.h */
      fprintf(stderr, "malloc() failed.
");
      goto CLEANUP;
      }

   /* Initialize the new node's payload. */       
   snprintf(newNode->payload.name, sizeof(newNode->payload.name), "%s", I__name);
   snprintf(newNode->payload.desc, sizeof(newNode->payload.desc), "%s", I__desc);
   newNode->payload.hours = I__hours;
   newNode->payload.workordernum = I__workordernum;

   /* Find the proper place to link this node */
   rCode=LIST_FetchParentNodeByName(*IO_head, I__name, &parent);
   switch(rCode)
      {
      case 0:
         break;

      case ENOENT:
         /* Handle empty list condition */ 
         newNode->next = NULL;
         *IO_head = newNode;
         rCode=0;
         goto CLEANUP;

      default:
         fprintf(stderr, "LIST_FetchParentNodeByName() reports: %d
", rCode);
         goto CLEANUP;
      }

<小时>

重要说明:LIST_FetchParentNodeByName() 函数会将父指针设置为指向列表中立即小于指定 I__name 的节点;- 除非 - 头节点大于指定的 I__name.对于这种特殊情况,LIST_FetchParentNodeByName() 会将父指针设置为 NULL.


Important note: The LIST_FetchParentNodeByName() function will set the parent pointer to the node in the list that is immediately less than the specified I__name; -unless- the head node is greater than the specified I__name. For this special case, LIST_FetchParentNodeByName() will set the parent pointer to NULL.

   /* Handle the case where all current list nodes are greater than the new node. */
   /* (Where the new node will become the new list head.) */
   if(NULL == parent)
      {
      newNode->next = *IO_head;
      *IO_head = newNode;
      goto CLEANUP;
      }

   /* Final case, insert the new node just after the parent node. */
   newNode->next = parent->next;
   parent->next = newNode;

CLEANUP:

   return(rCode);
   }

LIST_InsertNodeByName() 函数的调用方式与 LIST_InsertHeadNode() 或 LIST_InsertTailNode() 的调用方式相同.唯一的区别是,使用 LIST_InsertNodeByName() 时,新节点被插入到列表中按名称排序的位置;而不是在列表的头部或尾部.

The LIST_InsertNodeByName() function is called the same way LIST_InsertHeadNode() or LIST_InsertTailNode() is called. The only difference is that with LIST_InsertNodeByName(), the new node is inserted into its sorted (by name) location in the list; rather than at the list head or tail.

有时必须从列表中删除节点.这是通过定位要删除的节点,从列表中取消链接节点,然后删除节点及其有效负载来完成的.

There will be occasions when a node will have to be deleted from the list. This is done by locating the node to be deleted, unlinking the node from the list, and then deleting the node and it's payload.

首先,通过有效载荷名称字段定位特定节点的函数.

First, a function to locate a specific node by the payload name field.

int LIST_FetchNodeByName( 
      LIST_NODE_T  *I__head,
      const char   *I__name,
      LIST_NODE_T **_O_node,
      LIST_NODE_T **_O_parent
      )
   {
   int rCode=0;
   LIST_NODE_T *parent = NULL;
   LIST_NODE_T *curNode = I__head;

   /* Search the list for a matching payload name. */
   while(curNode)
      {
      if(0 == strcmp(curNode->payload.name, I__name))
         break;

      parent = curNode;   /* Remember this node; it will be the parent of the next. */
      curNode=curNode->next;
      }

   /* If no match is found, inform the caller. */
   if(NULL == curNode)
     {
     rCode=ENOENT;
     goto CLEANUP;
     }

   /* Return the matching node to the caller. */
   if(_O_node)
      *_O_node = curNode;

   /* Return parent node to the caller. */
   if(_O_parent)
      *_O_parent = parent;

CLEANUP:

   return(rCode);
   }

这是一个从列表中删除与特定负载名称匹配的节点的函数.

Here is a function that will delete a node from the list that matches a specific payload name.

   int LIST_DeleteNodeByName(
      LIST_NODE_T **IO_head,
      char         *I__name
      )
   {
   int rCode=0;
   LIST_NODE_T *parent;
   LIST_NODE_T *delNode = NULL;

   /* Find the node to delete. */
   rCode=LIST_FetchNodeByName(*IO_head, I__name, &delNode, &parent); 
   switch(rCode)
      {
      case 0:
         break;

      case ENOENT:
         fprintf(stderr, "Matching node not found.
");
         goto CLEANUP;

      default:
         fprintf(stderr, "LIST_FetchNodeByName() reports: %d
", rCode);
         goto CLEANUP;
      }

<小时>

重要提示:LIST_FetchNodeByName()函数会设置delNode的父指针;- 除非 - delNode 是头节点.对于这种特殊情况,LIST_FetchNodeByName() 会将父指针设置为 NULL.


Important note: The LIST_FetchNodeByName() function will set the parent pointer of the delNode; -unless- the the delNode is the head node. For this special case, LIST_FetchNodeByName() will set the parent pointer to NULL.

   /* Unlink the delNode from the list. */
   if(NULL == parent)
      *IO_head = delNode->next;
   else
      parent->next = delNode->next;

   /* Free the delNode and its payload. */
   free(delNode);

CLEANUP:

   return(rCode);
   }       

注意:以上所有代码都已经过测试,应该可以正常运行,可以下载为:23279119_List_101.c

(待续 - 根据要求...)

(To be continued- as per request...)

这篇关于在 C 中创建和理解结构的链表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆