单链表 C 反向数据读取

Posted

技术标签:

【中文标题】单链表 C 反向数据读取【英文标题】:singly linked linked list C reverse data reading 【发布时间】:2018-07-22 11:20:51 【问题描述】:

我有一个链接列表,我正试图反向阅读,但是我在这样做时遇到了问题。

这是我的结构。

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 * ======= 回溯:========= /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] ./最终[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 [堆栈] 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”是我的全局根变量

【问题讨论】:

“但它不起作用。”请解释如何它不起作用。请阅读How to Ask并提供minimal reproducible example。 你可以使用递归吗? (顺便说一句:给定的片段尝试打印链表,而不是读取它) 【参考方案1】:

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

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;
 

【讨论】:

【参考方案2】:

你可能想要这个:

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

【讨论】:

【参考方案3】:

正如@Sahyog Vishwakarma 已经回答的那样,一个简单的递归打印函数在节点不为空时重复调用自身是一种反向打印链接列表的聪明方法。由于我已经完成了一个简短的示例,因此我也会提供它以供您从中找到任何额外的好处。

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

#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-&gt;x 值用1 - 5 填充,调用时prn 以相反的顺序打印。

使用/输出示例

$ ./bin/prnrecrev
 5 4 3 2 1

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

【讨论】:

【参考方案4】:

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

 free(iter);

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

循环:

while(iter->next!=NULL)

正在检查 NULL 并且它没有找到 next 指针 NULL 并且您的程序最终释放了在外部 while 循环 if 的最后一次迭代中已经释放的内存。因此,您会收到 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;

【讨论】:

【参考方案5】:

你可以使用递归函数,比如



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

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

【讨论】:

你比我早 2 分钟。这是一种聪明的方法,除非你有 1,000,000 个节点,否则堆栈开销会有点冒险...... 是的,很聪明,但如果你能以迭代的方式做到这一点,就不要使用递归。

以上是关于单链表 C 反向数据读取的主要内容,如果未能解决你的问题,请参考以下文章

如何向后读取单链表?

单链表的读取插入与删除

反向读取Mysql数据库表结构到PowerDesigner中

数据结构之单链表反向查找

c语言如何从文件读入,并存放在链表中

大话数据结构---单链表