简体   繁体   中英

Linked List in C: What happens when we assign the current node to a temporary node pointer?

I have been given a task to write a function to reverse a doubly-linked list. I wrote the function and named it reverse() . The function is given below:

struct node* reverse(struct node** head_ref){
    struct node *current=*head_ref;
    struct node *tempNode=(struct node *)malloc(sizeof(struct node));
    if(current->next == NULL){
        return current;
    }
    while(current->next != NULL){
        //tempNode=current;
        tempNode->key=current->key;
        tempNode->data=current->data;
        tempNode->next=current->next;
        tempNode->prev=current->prev;
        current->next=current->prev;
        current->prev=tempNode->next;
        current=tempNode->next;
    }
    current->next=current->prev;
    current->prev=NULL;
    *head_ref =current;
    return current;
}

Where the struct node** head_ref parameter takes the reference of the head node &head as an argument. The function works fine. But my question here is instead of doing

tempNode->key=current->key;
tempNode->data=current->data;
tempNode->next=current->next;
tempNode->prev=current->prev;

Why can't I just do tempNode=current; ? When I try doing that instead my function doesn't produce the expected result.

tempnode = current assign pointer, it means that both tempnode and currect would be pointing to the same address in memory.

tempnode = curr; // tempnode = 1036;

  1020   1028   1036   1044
+------+------+------+------+
|      |      | curr |      |
+------+------+------+------+
                 ^
                 |
tempnode ---------

You just need tempnode to have same values so you have to do deep copy , its what exactly you are doing.

tempNode->key=current->key;
tempNode->data=current->data;
tempNode->next=current->next;         // You just copy the content of node
tempNode->prev=current->prev;         // But they are in another part of memory

  120    ....   1028   1036   1044
+------+------+------+------+------+
| temp |      | curr |      |      |
+------+------+------+------+------+

They have same values but they are pointing to another address.

I can see you are overwiting some pointers, you shouldnt do it becouse you are loosing address of allocated memory -> you cant free it anymore.

Store to some temp variable and after you remove it from node, you can free that memory.

Note that you really don't need to set tempNode->key or tempNode->data ; you leave those fields in the current node alone. However, if you did structure assignment ( *tempNode = *current; ) rather than pointer assignment ( tempNode = current; ), you would be OK.

Note that your code leaks a node every time the list is reversed; you don't free the tempNode that you allocate (probably because you've overwritten the allocated space with the pointer assignment). It would be better not to use malloc() for such a small structure; simply use struct node tempNode; and then tempNode = *current; and reference members by tempNode.next etc. There's then no need to free it.

In fact, given that there's really no need to copy the key and data, I'd probably use struct node *old_next = current->next; and struct node *old_prev = current->prev; and then flip the links: current->prev = old_next; current->next = old_prev; current = old_next; current->prev = old_next; current->next = old_prev; current = old_next; , as in the code shown. Keeping track of old_curr so that the correct value can be 'returned' (assigned to the pointer in the calling code via head_ref ) is the other wrinkle in this code. Note that it manages to avoid special-casing the empty list and the one-entry list.

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

struct node
{
    int key;
    int data;
    struct node *prev;
    struct node *next;
};

static struct node *reverse(struct node **head_ref)
{
    struct node *current = *head_ref;
    struct node *old_curr = 0;
    while (current != 0)
    {
        struct node *old_next = current->next;
        struct node *old_prev = current->prev;
        current->prev = old_next;
        current->next = old_prev;
        old_curr = current;
        current = old_next;
    }
    *head_ref = old_curr;
    return current;
}

static struct node *create(int key, int data)
{
    struct node *p = malloc(sizeof(*p));
    if (p != 0)
    {
        p->key = key;
        p->data = data;
        p->next = 0;
        p->prev = 0;
    }
    return p;
}

static struct node *insert_head(struct node *head, int key, int data)
{
    struct node *n = create(key, data);
    assert(n != 0);
    if (head != 0)
        head->prev = n;
    n->next = head;
    return n;
}

static void print_list(const char *tag, struct node *head)
{
    printf("%s: [", tag);
    const char *pad = "";
    while (head != 0)
    {
        printf("%s(%d => %d)", pad, head->key, head->data);
        pad = ",";
        head = head->next;
    }
    printf("]\n");
}

static void free_list(struct node *head)
{
    while (head != 0)
    {
        struct node *next = head->next;
        free(head);
        head = next;
    }
}

int main(void)
{
    for (int size = 0; size < 10; size++)
    {
        struct node *list = 0;
        for (int i = 0; i < size; i++)
            list = insert_head(list, i, (7 * i + 4 + size) % 10);
        print_list("New list", list);
        reverse(&list);
        print_list("Rev list", list);
        free_list(list);
    }
    return 0;
}

When run, the program produces the output:

New list: []
Rev list: []
New list: [(0 => 5)]
Rev list: [(0 => 5)]
New list: [(1 => 3),(0 => 6)]
Rev list: [(0 => 6),(1 => 3)]
New list: [(2 => 1),(1 => 4),(0 => 7)]
Rev list: [(0 => 7),(1 => 4),(2 => 1)]
New list: [(3 => 9),(2 => 2),(1 => 5),(0 => 8)]
Rev list: [(0 => 8),(1 => 5),(2 => 2),(3 => 9)]
New list: [(4 => 7),(3 => 0),(2 => 3),(1 => 6),(0 => 9)]
Rev list: [(0 => 9),(1 => 6),(2 => 3),(3 => 0),(4 => 7)]
New list: [(5 => 5),(4 => 8),(3 => 1),(2 => 4),(1 => 7),(0 => 0)]
Rev list: [(0 => 0),(1 => 7),(2 => 4),(3 => 1),(4 => 8),(5 => 5)]
New list: [(6 => 3),(5 => 6),(4 => 9),(3 => 2),(2 => 5),(1 => 8),(0 => 1)]
Rev list: [(0 => 1),(1 => 8),(2 => 5),(3 => 2),(4 => 9),(5 => 6),(6 => 3)]
New list: [(7 => 1),(6 => 4),(5 => 7),(4 => 0),(3 => 3),(2 => 6),(1 => 9),(0 => 2)]
Rev list: [(0 => 2),(1 => 9),(2 => 6),(3 => 3),(4 => 0),(5 => 7),(6 => 4),(7 => 1)]
New list: [(8 => 9),(7 => 2),(6 => 5),(5 => 8),(4 => 1),(3 => 4),(2 => 7),(1 => 0),(0 => 3)]
Rev list: [(0 => 3),(1 => 0),(2 => 7),(3 => 4),(4 => 1),(5 => 8),(6 => 5),(7 => 2),(8 => 9)]

Note that I had to create 34 lines of support code for a 16-line function. Granted, it wasn't all that hard, but an MCVE ( Minimal, Complete, Verifiable Example ) would have provided that infrastructure so I didn't have to do it. Please remember to create an MCVE in future.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM