【问题标题】:Why isn't my reverse(); function working?为什么不是我的 reverse();功能工作?
【发布时间】:2021-04-10 15:24:21
【问题描述】:

我正在用 C 语言编写一个用于反转循环单链表的程序。由于某种原因,我不断遇到分段错误。我确定问题出在reverse 函数上,因为我尝试对函数调用进行注释,程序运行良好。

对于我的 reverse() 函数,我使用了 3 个指针:prevnextcurr。逻辑是我将运行一个循环,直到curr 获取head 的地址,因为它是一个循环链表,它将存储在最后一个节点的link 部分。我将使用prev 指针不断更新curr->link,这会将其链接从下一个节点更改为上一个节点。

当循环中断时,head->link = prev;head = prev; 将更新各自的地址,使它们指向反向列表的第一个节点。

//reversing CLL

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

struct node {
    int data;
    struct node *link;
} *head;

void reverse() {
    struct node *prev = NULL, *curr = head, *next;
        
    while (curr != head) {
        next = curr->link;
        curr->link = prev;
        prev = curr;
        curr = next;
    }
        
    head->link = prev;
    head = prev; 
}

void createList(int n) {
    int i, data;    
    
    head = (struct node *)malloc(sizeof(struct node));
        
    struct node *ptr = head, *temp;
            
    printf("Enter data of node 1\t");
    scanf("%d", &data);
            
    head->data = data;
    head->link = NULL;
            
    for (i = 2; i <= n; i++) {
        temp = (struct node *)malloc(sizeof(struct node));
                        
        printf("Enter data of node %d\t", i);
        scanf("%d", &data);
                        
        temp->data = data;
        temp->link = NULL;
                        
        ptr->link = temp;
        ptr = ptr->link;
    }
    ptr->link = head;
}

void disp() {
    struct node *ptr = head;
        
    do {
        printf("%d\t", ptr->data);   //gdb debugger shows problem is in this line
        ptr = ptr->link;
    } while (ptr != head);
}

int main() {
    int n;
        
    printf("Enter no of nodes to be created\t");
    scanf("%d", &n);
        
    createList(n);
            
    printf("\n\nList is displayed below!\n");
        
    disp();
            
    printf("\n\nReversing list ...\n");
            
    reverse();   // on commenting this call, disp() function 
                 // works accurately showing node data non-reversed
                  
    disp();
            
    printf("\n\nList successfully reversed!\n");
}

【问题讨论】:

  • 调试器显示错误出在 printf("%d\t",ptr->data);而且我一辈子都想不通。
  • @Mohsin while(curr!=head) 的循环条件何时计算为真?
  • @VladfromMoscow 循环从指向第一个节点的 curr 开始。当curr获取head的地址时,它会一直运行到最后一个节点,因为它是一个循环链表,所以最后一个节点的“链接”部分指向第一个节点。
  • 您错过了弗拉德评论的重点。 curr = head; while(curr!=head)。使用该代码,while 条件总是立即为假,因此循环体永远不会运行。

标签: c reverse singly-linked-list circular-list function-definition


【解决方案1】:

对于初学者来说,使用全局变量head 是个坏主意

struct node {
    int data;
    struct node *link;
} *head;

在这种情况下,函数依赖于全局变量,您不能在程序中使用多个列表。

由于这个初始化

struct node *prev = NULL, *curr = head, *next;
                          ^^^^^^^^^^^^

while 循环的条件

while (curr != head) {

is 永远不会计算为 true,因为最初指针 curr 等于指针 head

此外,如果列表为空,则此语句

head->link = prev;

调用未定义的行为。

这是一个演示程序,展示了如何在 main 中声明列表然后反转。

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

struct node 
{
    int data;
    struct node *link;
};

size_t assign( struct node **head, const int a[], size_t n )
{
    while ( *head )
    {
        struct node *tmp = *head;
        *head = ( *head )->link;
        free( tmp );
    }
    
    size_t total = 0;
    
    struct node *first = NULL;
    
    while ( total < n && ( *head = malloc( sizeof( struct node ) ) ) != NULL )
    {
        ( *head )->data  = a[total];
        ( *head )->link  = NULL;
        ++total;
        
        if ( first == NULL ) first = *head;
        
        head = &( *head )->link;
    }
    
    if ( first != NULL ) *head = first;
    
    return total;
}

void display( const struct node *head )
{
    if ( head != NULL )
    {
        const struct node *current = head;
        do
        {
            printf( "%d -> ", current->data );
        } while ( ( current = current->link ) != head );
    }       
    
    puts( "null" );
}

struct node * reverse( struct node **head )
{
    if ( *head )
    {
        struct node *last = *head;
        struct node *prev = NULL;

        while ( ( *head )->link != last )
        {
            struct node *current = *head;
            *head = ( *head )->link;
            current->link = prev;
            prev = current;
        }
        
        ( *head )->link = prev;
        last->link = *head;
    }
    
    return *head;
}

int main(void) 
{
    struct node *head = NULL;
    
    int a[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    
    assign( &head, a, sizeof( a ) / sizeof( *a ) );
    
    display( head );
    
    display( reverse( &head ) );
    
    display( reverse( &head ) );
    
    return 0;
}

程序输出是

0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> null
9 -> 8 -> 7 -> 6 -> 5 -> 4 -> 3 -> 2 -> 1 -> 0 -> null
0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> null

【讨论】:

  • 好的,我有一些问题。请多多包涵。 1. 你能否详细说明为什么让“head”全球化是一种不好的做法? 2. 为什么在这个程序中使用数组? 3. 为什么你使用*head 而不是'head'?我将循环更改为执行 { } while { },这解决了 curr!=head 初始化问题,但我想知道您为什么要编写这样的代码。我的方法不好吗?
  • @Mohsin 使用全局变量是个坏主意,因为在这种情况下,您只能拥有一个具有该名称的全局变量。因此,您列表的用户将无法同时拥有例如两个列表。函数也将取决于全局变量。我只使用了一个数组来简化使用任何数据填充列表,
【解决方案2】:

reverse() 函数中的循环立即退出,因为curr 被初始化为head 的值,所以测试while (curr != head) 在第一次迭代时为假。

reverse()然后将head-&gt;link设置为NULL,最后head也设置为NULLprev的初始值),这解释了后续disp()函数中的分段错误,其中您使用无法处理空列表的do { } while (pre != head)

这是修改后的版本:

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

struct node {
    int data;
    struct node *link;
};

struct node *reverse(struct node *head) {
    struct node *prev = NULL, *curr = head;

    if (head) {
        do {
            struct node *next = curr->link;
            curr->link = prev;
            prev = curr;
            curr = next;
        } while (curr != head);
        curr->link = prev;
        head = prev;
    }
    return head;
}

struct node *createList(int n) {
    struct node *head = NULL, *tail = NULL, *temp;
    int i;

    for (i = 1; i <= n; i++) {
        temp = (struct node *)malloc(sizeof(struct node));
        temp->data = 0;
        temp->link = NULL;

        printf("Enter data of node %d\t", i);
        scanf("%d", &temp->data);

        if (head == NULL) {
            head = temp;
        } else {
            tail->link = temp;
        }
        tail = temp;
        temp->link = head;
    }
    return head;
}

void disp(struct node *head) {
    if (head) {
        struct node *ptr = head;
        do {
            printf("%d\t", ptr->data);
            ptr = ptr->link;
        } while (ptr != head);
    }
}

int main() {
    struct node *head;
    int n = 0;

    printf("Enter no of nodes to be created\t");
    scanf("%d", &n);

    head = createList(n);

    printf("\n\nList is displayed below!\n");
    disp(head);

    printf("\n\nReversing list ...\n");

    head = reverse(head);

    disp(head);

    printf("\n\nList successfully reversed!\n");

    // should free the list
    return 0;
}

【讨论】:

  • 我理解了我在 while(curr!=head) 语句中的错误。我不太了解分段错误背后的原因,但我会自己进行研究。你能向我解释一下 if (head) 是什么意思吗?我到处都看到过,但我不明白。谢谢。
  • @Mohsin:在reverse 函数的开始处,prev 设置为NULL。由于循环根本不迭代,最后两个语句head-&gt;link = prev; head = prev;head-&gt;link 设置为NULL,最后将head 设置为NULL。然后调用disp() 将其局部变量ptr 初始化为head,即NULLprintf("%d\t", ptr-&gt;data); 会导致分段错误,因为您取消引用试图访问ptr-&gt;data 的空指针。 if (head) 测试指针 head 是否为真值。如果 非空,则指针为真。测试相当于if (head != NULL)
猜你喜欢
  • 2011-06-08
  • 2014-05-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-09
  • 1970-01-01
  • 2021-02-01
  • 1970-01-01
相关资源
最近更新 更多