插入链表中总是返回最后一个节点,而不是当前节点

时间:2019-02-15 18:55:47

标签: c

因此,我正在编写一个创建动态列表的程序,它在最后一个节点之后插入一个新节点,并最终打印出新创建的列表。我希望当我打印它时,它实际上连续打印当前节点的编号(例如Info:1,Info:2,Info:3),但事实证明它总是打印最后一个节点。我猜这是一个指针错误,但实际上找不到。

#include <stdio.h>
#include <stdlib.h>

struct node{
  int info;
  struct node* pNext;
};

typedef struct node Node;

void tail_insertion(Node* pFirst, Node* pLast);
void print_list(Node* pFirst);

static int n=3;

int main()
{
    Node *pFirst=NULL, *pLast=NULL;
    tail_insertion(pFirst, pLast);
}

void tail_insertion(Node* pFirst, Node* pLast){
  // Creation of a new node in the heap
  Node *pNew = (Node*) malloc(sizeof(Node));
  pNew->info=0;
  pNew->pNext= NULL;

  for(int i=0; i<3;i++){
    if(pFirst == NULL){// No node in the list
      pNew->info++;
      pFirst = pNew; // The first node is the newly created one
      pLast = pNew; // The last node is the newly created one
      printf("Ok the list was empty\n");
    }
    else{
      // Else, there is already at least one node in the list
      pNew->info++;
      pLast-> pNext= pNew; // the last node becomes the second one
      pLast= pNew; // The last node is the newly created one
      printf("Ok the list wasn't empty\n");
    }
  }
  print_list(pFirst);
  return;
}


void print_list(Node* pFirst){
  if(pFirst == NULL){
    printf("No node in the list!\n");
  }
  else{
    // New pointer used to scan the list.
    Node* pScan = pFirst;
    do{
      printf("Info: %d\n", pScan->info);
      // ptrScan is updated to point to the next node in the
      // list
      pScan = pScan->pNext;
    }while(pScan!= NULL && --n); //NULL when this was the last node
  }
  return;
}

打印如下:

Ok the list was empty
Ok the list wasn't empty
Ok the list wasn't empty
Info: 3
Info: 3
Info: 3

2 个答案:

答案 0 :(得分:2)

两个问题:

  • 您没有将指针传递给列表指针(Node *-> Node **
  • 您正在尝试将相同 Node插入3次(将malloc()插入循环)

我猜想static int n = 3while ... && --n)可能是为了“修复” print_list()中的无穷循环而进行的一次拼命尝试,因为pscan->pNext产生了pscan用原始代码...

修复并清理代码:

#include <stdio.h>
#include <stdlib.h>

typedef struct node Node;
typedef struct node {
  int   info;
  Node* pNext;
} Node;

static void print_list(Node* pFirst) {
  // New pointer used to scan the list.
  Node* pScan = pFirst;
  while (pScan) {
    printf("Info: %d\n", pScan->info);
    // ptrScan is updated to point to the next node in the
    // list
    pScan = pScan->pNext;
  }
}

static void tail_insertion(Node** pFirst, Node** pLast) {
  for (int i=0; i<3; i++) {
    // Creation of a new node in the heap
    Node *pNew  = malloc(sizeof(Node));
    pNew->info  = i + 1;
    pNew->pNext = NULL;

    if (*pFirst == NULL) {    // No node in the list
      *pFirst = pNew;         // The first node is the newly created one
      *pLast  = pNew;         // The last node is the newly created one
      printf("Ok the list was empty\n");
    } else {                  // Else, there is already at least one node in the list
      (*pLast)->pNext = pNew; // the last node becomes the second one
      *pLast          = pNew; // The last node is the newly created one
      printf("Ok the list wasn't empty\n");
    }
  }
  print_list(*pFirst);
}

int main(int argc, char *argv[]) {
  Node *pFirst = NULL;
  Node *pLast  = NULL;
  tail_insertion(&pFirst, &pLast);
}

试运行:

$ gcc -Wall -Werror -o dummy dummy.c
$ ./dummy
Ok the list was empty
Ok the list wasn't empty
Ok the list wasn't empty
Info: 1
Info: 2
Info: 3

答案 1 :(得分:2)

常规代码分析

让我们考虑 tail_insertion(...)以及您要执行的操作:

  • 您创建一个新节点;
  • 您给它正确的值;
  • 您启动for循环的目的是根据特定条件创建更多节点;
  • 您可以打印整个列表以检查其是否有效;

现在,查看您的代码 tail_insertion(...) 功能已删除注释):

Node *pNew = (Node*) malloc(sizeof(Node));

pNew->info=0;
pNew->pNext= NULL;

for(int i=0; i<3;i++){

    if(pFirst == NULL){

        pNew->info++;

        pFirst = pNew;
        pLast = pNew;

        printf("Ok, the list was empty.\n");

    } else {

        pNew->info++;

        pLast-> pNext= pNew;
        pLast= pNew;

        printf("Ok, the list wasn't empty.\n");

    }

}

print_list(pFirst);

看到任何问题了吗?没有?好吧,让我们继续。

调试

我们将逐步调试此功能。除了程序修复之外,它对于学习过程也很重要:

  • 创建节点
    • 您创建pNEW。它是空的。
    • 让我们给它地址0x01(虚构);
    • 已将0分配给pNew->info
    • 已将NULL分配给pNew->pNext
  • for 循环的开始-尝试为列表创建3个节点:
    • i = 0
      • 您检查列表是否为空。是:
      • 您将1添加到pNew->info值中。 pNew的地址为0x01;
      • pFirst被分配为指向地址0x01
      • 由于列表为空,
      • pLast被分配为指向地址0x01
      • 您打印一条消息,说列表为空;
    • i = 0的末尾,列表如下:
      • pFirst指向地址0x01
      • pLast指向地址0x01
      • 0x01->info1
    • i = 1
      • 您检查列表是否为空。不是:
      • 您将1添加到pNew->info值中。 pNew的地址为0x01。从来没有创建另一个节点;
      • pLast->pNext被分配为指向地址0x01
      • pLast被分配为指向地址0x01
      • 您打印一条消息,说明列表不为空;
    • i = 1的末尾,列表如下:
      • pFirst指向地址0x01
      • pLast指向地址0x01
      • pLast->pNext指向地址0x01
      • 0x01->info2;
    • i = 2
      • 您检查列表是否为空。不是:
      • 您将1添加到pNew->info值中。 pNew的地址为0x01。从来没有创建另一个节点;
      • pLast->pNext被分配为指向地址0x01
      • pLast被分配为指向地址0x01
      • 您打印一条消息,说明列表不为空;
    • i = 2的末尾,列表如下:
      • pFirst指向地址0x01
      • pLast指向地址0x01
      • pLast->pNext指向地址0x01
      • pLast->pNext->pNext指向地址0x01
      • 0x01->info3;
    • for循环的结尾;
  • 打印结果列表
    • 致电print_list(...)

结论

到目前为止,您可能已经看到出了什么问题:您尚未在for循环内创建新节点。您在循环之外创建了它。这样便进行了一个重复的过程,即将具有相同地址的相同节点添加到列表中

此外,您在使用info的值时遇到了问题。但这要归功于使用了与上述相同的节点。只需对此增量逻辑进行一些调整即可设置info的值,以适应需要修改以修复同一节点的使用并使所有工作正常的情况。

代码修复

使用您自己的代码,应修复tail_insertion(...)使其类似于以下代码。有评论可帮助您了解更改。

Node *pNew; /* Declaring pNew *without* assignment. */

for(int i = 0; i < 3; i++){

    pNew = (Node*) malloc(sizeof(Node)); /* Allocation of new memory address. */

    pNew->info  = i; /* pNew->info works like an ID in you current code. */

    pNew->pNext = NULL; /* It is proper to assign NULL */

    if(pFirst == NULL){

        pNew->info++;

        pFirst       = pNew;
        pLast        = pNew;

        printf("The list was empty.\n");

    } else {

        pNew->info++;

        pLast->pNext = pNew;
        pLast        = pNew;

        printf("The list wasn't empty.\n");

    }
}

print_list(pFirst);

请注意,由于不必要,我已删除了return(...)函数。没必要,因为tail_insertion(...)的类型为void

此外,截至我阅读时,pNew->info正在存储该节点的ID。因此,将i分配给它应该不是问题。如有必要,请进行适当的更改。

最后的笔记

有评论说您不需要pLast。确实如此,您需要考虑:

  • 如果仅使用pFirst,则需要确保在pFirst->pNext中插入新节点之后,您要执行pFirst->pNext->pNext = NULL。这样,您可以通过与NULL进行比较来知道何时到达列表末尾;
  • 如果继续使用pLast,则可以与pLast进行比较,以了解该地址是否是列表中的最后一个地址。显然,修复后,您也可以将其与NULL进行比较;
  • 考虑到尾部插入,从长远来看,pLast可以大大简化操作并降低CPU使用率;

您可能知道,但应该将print_list(pFirst);放在main(...)函数中,以使事情井井有条。而且,这使我的“代码OCD”达到了绝望的新境界! ;-)