单链接链表C反向数据读取

时间:2018-02-12 10:12:19

标签: c linked-list singly-linked-list

我有一个链接列表,我试图反过来阅读,但是我这样做有问题。

这是我的结构。

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

问题在于我的阅读功能。 这是功能:

void read(){
    node *iter;
    while(r->next!=NULL){
        iter=r;
        while(iter->next!=NULL) iter=iter->next;
        printf("%d",iter->x);
        free(iter);
    }
    printf("%d",r->x);
}

我迭代到列表的末尾,然后我读取数据并在之后删除它。并循环它。 但它不起作用。谁能请我解释一下我做错了什么? 该程序的输出也是:

  

*`./final'错误:双重免费或损坏(fasttop):0x00000000022e24a0 *   ======= Backtrace:========= /lib/x86_64-linux-gnu/libc.so.6(+0x777e5)[0x7faccbe7b7e5]   /lib/x86_64-linux-gnu/libc.so.6(+0x8037a)[0x7faccbe8437a]   /lib/x86_64-linux-gnu/libc.so.6(cfree+0x4c)[0x7faccbe8853c]   ./final[0x400942] ./ final [0x400a06]   /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7faccbe24830]   ./final[0x4006b9]   =======记忆地图:======== 00400000-00401000 r-xp 00000000 08:01 8257622 / home / r4g3 / Desktop / 2U / final   00600000-00601000 r - p 00000000 08:01 8257622
  / home / r4g3 / Desktop / 2U / final 00601000-00602000 rw-p 00001000 08:01   8257622 / home / r4g3 / Desktop / 2U / final   022d0000-02302000 rw-p 00000000 00:00 0
  [堆] 7facc4000000-7facc4021000 rw-p 00000000 00:00 0   7facc4021000-7facc8000000 --- p 00000000 00:00 0   7faccb8e5000-7faccb8fb000 r-xp 00000000 08:01 39326162
  /lib/x86_64-linux-gnu/libgcc_s.so.1 7faccb8fb000-7faccbafa000 --- p   00016000 08:01 39326162
  /lib/x86_64-linux-gnu/libgcc_s.so.1 7faccbafa000-7faccbafb000 rw-p   00015000 08:01 39326162
  /lib/x86_64-linux-gnu/libgcc_s.so.1 7faccbafb000-7faccbc03000 r-xp   00000000 08:01 39321605
  /lib/x86_64-linux-gnu/libm-2.23.so 7faccbc03000-7faccbe02000 --- p   00108000 08:01 39321605
  /lib/x86_64-linux-gnu/libm-2.23.so 7faccbe02000-7faccbe03000 r - p   00107000 08:01 39321605
  /lib/x86_64-linux-gnu/libm-2.23.so 7faccbe03000-7faccbe04000 rw-p   00108000 08:01 39321605
  /lib/x86_64-linux-gnu/libm-2.23.so 7faccbe04000-7faccbfc4000 r-xp   00000000 08:01 39321685
  /lib/x86_64-linux-gnu/libc-2.23.so 7faccbfc4000-7faccc1c4000 --- p   001c0000 08:01 39321685
  /lib/x86_64-linux-gnu/libc-2.23.so 7faccc1c4000-7faccc1c8000 r - p   001c0000 08:01 39321685
  /lib/x86_64-linux-gnu/libc-2.23.so 7faccc1c8000-7faccc1ca000 rw-p   001c4000 08:01 39321685
  /lib/x86_64-linux-gnu/libc-2.23.so 7faccc1ca000-7faccc1ce000 rw-p   00000000 00:00 0 7faccc1ce000-7faccc340000 r-xp 00000000 08:01   13631793个
  /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21   7faccc340000-7faccc540000 --- p 00172000 08:01 13631793
  /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21   7faccc540000-7faccc54a000 r - p 00172000 08:01 13631793
  /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21   7faccc54a000-7faccc54c000 rw-p 0017c000 08:01 13631793
  /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21   7faccc54c000-7faccc550000 rw-p 00000000 00:00 0   7faccc550000-7faccc576000 r-xp 00000000 08:01 39321617
  /lib/x86_64-linux-gnu/ld-2.23.so 7faccc74f000-7faccc755000 rw-p   00000000 00:00 0 7faccc774000-7faccc775000 rw-p 00000000 00:00 0   7faccc775000-7faccc776000 r - p 00025000 08:01 39321617
  /lib/x86_64-linux-gnu/ld-2.23.so 7faccc776000-7faccc777000 rw-p   00026000 08:01 39321617
  /lib/x86_64-linux-gnu/ld-2.23.so 7faccc777000-7faccc778000 rw-p   00000000 00:00 0 7ffd85b05000-7ffd85b26000 rw-p 00000000 00:00 0
  [stack] 7ffd85b97000-7ffd85b9a000 r - p 00000000 00:00 0
  [vvar] 7ffd85b9a000-7ffd85b9c000 r-xp 00000000 00:00 0
  [vdso] ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0
  [vsyscall] 10Aborted

P.S:" r"是我的全局根变量

5 个答案:

答案 0 :(得分:2)

释放节点后,将其指向它的前一个节点next指针设置为NULL。 所以,如果你这样做:

 free(iter);

指向next的上一个节点iter指针应设置为NULL
free()函数不会更改指针本身的值,因此前一个节点next指针仍指向相同(现在无效)的位置。

循环:

while(iter->next!=NULL)

正在检查NULL并且它找不到next指针NULL,并且您的程序最终释放已在外部while循环的最后一次迭代中释放的内存如果。因此,您收到double free错误。

要解决此双重免费问题,请跟踪要释放的节点的上一个节点,并在调用free()后将前一个节点next指针设置为NULL。像这样:

    node *prev = NULL;
    while(iter->next!=NULL) { 
         prev = iter;
         iter = iter->next;
    }
    printf("%d", iter->x);
    free(iter);
    if (prev)
        prev->next = NULL;

答案 1 :(得分:1)

你可以使用像

这样的递归函数


    void read(node* p){
       if(p->next != NULL)
           read(p->next);
       printf("%d ", p->x);
    }

这将首先到达链表的末尾并显示从最后到第一个的值。它不会释放内存,但您的问题将解决以相反的顺序打印链接列表。

答案 2 :(得分:1)

正如@Sahyog Vishwakarma已经回答的那样,一个简单的递归打印函数在节点不为NULL时反复调用自身,这是一种反向打印链表的聪明方法。由于我已经做了一个简短的例子,我将提供它以及你可能在其中找到的任何其他好处。

该示例仅使用静态节点数组来创建列表和短打印函数,例如

#include <stdio.h>

#define NNODES 5

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

/* simple recursive function to print list in reverse */
void prn (node *l)
{
    if (l) {                    /* if not NULL */
        if (l->next)            /* if next not NULL */
            prn (l->next);      /* call prn (next node) */
        printf (" %d", l->x);   /* print values on way out */
    }       
}

int main (void) {
    /* short example with static array of nodes */
    node a[NNODES] = {{ .x = 0, .next = NULL }};

    /* initialize the linked list */
    for (int i = 0; i < NNODES; i++) {
        a[i].x = i + 1;
        if (i < NNODES - 1)
            a[i].next = &a[i+1];
    }
    prn (a);          /* reverse-print the list */
    putchar ('\n');   /* tidy up with a newline */
    return 0;
}

node->x值填充1 - 5,调用prn时会按相反的顺序打印。

示例使用/输出

$ ./bin/prnrecrev
 5 4 3 2 1

注意:每次递归调用都需要创建自己的函数堆栈,因此当列表增长时,使用递归函数时函数调用开销随之增长。作为一般经验法则,你可以在迭代解决方案或程序解决方案之间进行选择,你最好选择程序解决方案 - 不总是,但通常都是这样)

答案 3 :(得分:0)

我刚刚意识到你是数据结构的新手,可能对递归并不那么熟悉。因此,我决定编写一个小程序,它可以帮助您理解链接列表并在没有递归的情况下将其反转。如果您对该区域有更多信息,请告知我们。

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

void createList(node *h, int length)
{
   int c = 0;
   node *temp = h;
   while(c < length)
   {
     temp->next = (node *)malloc(sizeof(node));
     temp = temp->next;
         temp->x = ++c;
         temp->next = NULL;
   }
}

void PrintList(node *h) //print all the nodes
{
    while (h != NULL)
    {
       printf("%d\n", h->x);
       h = h->next;
     }
 }

node * reverseList(node *h)
{
  node *p, *q, *l;
  p = h;
  q = p->next;
  l = q->next;
  p->next = NULL;
  while(l != NULL)
  {
    q->next = p;
    p = q;
    q = l;
    l = l->next;
  }
   q->next = p;
   return q;
}

void deleteList(node *h)
{
   node *t = h;
   h = h->next;
   free(t);
   while(h != NULL)
   {
     t = h;
         h = h->next;
     free(t);
   }
}

 int main(void)
 {
   node *head = (node *)malloc(sizeof(node));
   head->x = 0;
   head->next = NULL;
   createList(head,10);//create a list with another 10 node total 11
   PrintList(head);
   printf("Now we are reversing the list\n");
   head = reverseList(head);
   PrintList(head);
   deleteList(head);//finally delete the list
       head = NULL;
       return 0;
 }

答案 4 :(得分:0)

你可能想要这个:

void read() {
  node *iter;
  while (r->next != NULL) {
    iter = r;    
    node *current = NULL;
    while (iter->next != NULL) {
      current = iter;
      iter = iter->next;
    }

    printf("%d ", iter->x);
    current->next = NULL;
    free(iter);
  }

  if (r != NULL)
  {
    printf("%d ", r->x);
    free(r);
    r = NULL;
  }
}

read函数从后面打印整个列表,删除整个列表。显然是您原始代码尝试(和失败)的原因。

这当然效率很低,因为你需要一遍又一遍地找到列表末尾的结尾,但是使用单链表你别无选择。

顺便说一句:拥有一个全局变量r设计很差,选择像r之类的名称是一个坏主意,我将其称为listHead