在C语言中创建和理解结构的链表

在C语言中创建和理解结构的链表,c,list,pointers,struct,linked-list,C,List,Pointers,Struct,Linked List,我很难同时掌握struct和链表数据结构的概念。例如,假设我们有这样的代码:一个struct,它有一个worker的内容和这些结构的链接列表,其中包含每个worker的节点和指向下一个节点(?)的指针 问题是如何创建一个始终在列表开头添加节点的方法,一个使用用户定义的workordernum将节点添加到列表中任何位置(中间)的方法,以及一个始终将其放在末尾的方法 我不太理解->和*的正确用法。我确实在网上读过关于创建头部和尾部节点的内容,但我没有完全正确地理解它的用法,因为它们有一个struct

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

问题是如何创建一个始终在列表开头添加节点的方法,一个使用用户定义的
workordernum
将节点添加到列表中任何位置(中间)的方法,以及一个始终将其放在末尾的方法

我不太理解
->
*
的正确用法。我确实在网上读过关于创建头部和尾部节点的内容,但我没有完全正确地理解它的用法,因为它们有一个
struct
用于列表,一个
struct
用于节点

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

我理解,每次添加、删除或移动节点时,都必须跟踪,这意味着每次调用这些方法时,我们必须有一个变量来跟踪数量。因此,如果列表中有一个节点已准备就绪,其顺序将为1,然后我们在开始处添加另一个节点,如何将顺序号1更改为2,并将其添加为1

或者,如果只有一个指针,节点->下一个->下一个->下一个如何工作?那我们怎么把它们都打印出来呢?因为我们不能对循环使用

这些是我在代码方面无法理解的概念如果可能的话,请您花时间解释一下,而不是仅仅给出代码,我将不胜感激。因为我必须将所学知识应用于移动和删除节点。我想自己学。如果必须给出一些代码示例,那么这很好,但请不要为我发布所有答案代码

-多谢各位

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

编辑:我确实理解指针是一个地址,
->
与“指向”成员有关。我的意思是,我了解所有的基本知识,但我的理解不够坚定,否则我可以做我需要帮助的事情

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

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;
}

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

链接列表-101-单链接列表

   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\n", rCode);
      goto CLEANUP;
      }
这是一个很长的答案。我之所以如此详细,是因为有大量的链表问题,我希望在适当的背景下及时回答

当我学习C时,我很难使用指针。然而,在实现了链表之后,我终于开始掌握指针的概念。主链表在C语言中是一件好事,它将帮助您熟悉指针。当事情看起来令人困惑时,拿一支铅笔和一张纸,画出一张列表图以及与节点相关的链接。当我使用复杂的列表实现时,我偶尔会这样做

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

链表有两种基本类型;单链接列表和双链接列表。不同之处在于,单链表只能在一个方向上遍历;而双链表可以在两个方向上遍历

单链接列表通过其“head”指针或指向head list节点的指针进行访问。双链接列表也可以通过其“head”指针或“tail”指针进行访问

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

以下是单链接列表的布局:

            Node #1      Node #2      Node #3      EndOfList
            ----------   ----------   --------     ---------
HEADPTR-->  NEXTPTR-->   NEXTPTR-->   NEXTPTR-->   NULL
            DataPayload  DataPayload  DataPayload
列表中的每个节点及其数据有效负载都是单独分配的。节点结构(C中)可能如下所示:

    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;
要初始化上述结构的单链接列表,请执行以下操作:

LIST_NODE_T *listHead = NULL;
“listHead”现在是指向链接列表(没有节点)的指针

以下是如何在此列表的开头添加新节点:

int LIST_InsertHeadNode(
      LIST_NODE_T **IO_head,

问:为什么这里需要“双指针”(即:列表节点**…)?为什么不使用“单级”指针(即:列表\节点\节点*…)

答:指向列表头的“单个”指针不足以执行此操作。具体而言,此操作指定了一个新的“头节点”。这意味着此函数需要修改指向头节点的指针

之前:

                         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指向“Node#Y”,然后在后面,HEADPTR指向“New Node”。调用此函数时,会传入listHead指针的地址,从而允许此函数更改listHead指针指向的位置。换句话说,listHead指针的地址会传入此函数,而表示为指向listHead指针的指针(指向指针的指针)。这就是为什么它是“双指针”



问:这是什么“goto CLEANUP;”业务

A: C语言不同于C++和java,没有“异常”的概念。C中的错误检查是关键的。函数可能会失败,如果失败,代码必须尽可能优雅地处理它。“goto CLEANUP”语句会导致正常程序流跳过代码跳转到“CLEANUP:”

      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.\n");
      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 head node. */
   newNode->next = *IO_head;
   *IO_head = newNode;

CLEANUP:

   return(rCode);
   }
#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\n", rCode);
      goto CLEANUP;
      }

CLEANUP:

   return(rCode);
   }
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\n",
            cur->payload.name,
            cur->payload.desc,
            cur->payload.hours,
            cur->payload.workordernum
            );
       cur=cur->next;
       }

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

   return(rCode);
   }
#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\n", rCode);
      goto CLEANUP;
      }

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

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

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

CLEANUP:

   return(rCode);
   }
   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);
      }
   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\n", rCode);
      goto CLEANUP;
      }
   /* 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.\n");
      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
      *IO_head = newNode;

CLEANUP:

   return(rCode);
   }
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);
   }
   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.\n");
      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\n", rCode);
         goto CLEANUP;
      }
   /* 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);
   }
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);
   }
   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.\n");
         goto CLEANUP;

      default:
         fprintf(stderr, "LIST_FetchNodeByName() reports: %d\n", rCode);
         goto CLEANUP;
      }
   /* 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);
   }