Search code examples
c++listpointerssingly-linked-list

Segmentation fault error singly linked lists C++


Why is this getting an error:

int main(){
    lista *head, *x;
    head = nullptr;
    const int len = 3;
    int y[len] = {13, 4, 32};
    for(int i = len - 1; i >= 0; i--){
        x = new lista;
        x -> val = y[i];
        x -> next = head;
        head = x;
    }
    lista *p = head;
    // Changing lines are the next ones
    while(p->next != nullptr){
        p = p->next;
    }
    delete p;
    p = nullptr;

    print_lista(head);
    return 0;
}

But this is not:

int main(){
    lista *head, *x;
    head = nullptr;
    const int len = 3;
    int y[len] = {13, 4, 32};
    for(int i = len - 1; i >= 0; i--){
        x = new lista;
        x -> val = y[i];
        x -> next = head;
        head = x;
    }
    lista *p = head;
    // Changing lines are the next ones
    while(p->next->next != nullptr){
        p = p->next;
    }
    delete p->next ;
    p->next = nullptr;

    print_lista(head);
    return 0;
}

It doesn't work also in this way

while(p->next != nullptr){
    p = p->next;
}
p = p->next;
delete p;
p = nullptr;

This is the given error:

the monitored command dumped core

Segmentation fault

Isn't that the same thing? In the first case p is the next value of the second last element, in the second one p is the next value of the third last element, so p->next should be the next value of the second last element. This is the structure:

struct lista{
    int val;
    lista *next;
};

The error occurs in the 'delete...;' line

EDIT for Ben Voigt

Why? If I run a test like this:

lista *p = head;
while (p->next->next != nullptr){
    p = p->next;
}
cout << p->next;
lista *z = head;
while (z->next != nullptr){
    z = z->next;
}
cout << endl << z;

Both p and z are 0x5575909e3eb0, aren't them the same?


Solution

  • The problem is that in the failing code, you're deallocating the last item in the list, without removing it from the list. The result is a list containing a dangling pointer, so later operations that walk the list have undefined behavior.

    The working code does detach it from the list.


    To answer your thought experiment, let's make it even simpler.

    std::cout << head << '\n';
    lista* z = head;
    std::cout << z << '\n';
    
    z = nullptr;
    // now how many items are in the list?
    std::cout << head << '\n';
    std::cout << z << '\n';
    
    // are `head` and `z` really equivalent?
    z = head;
    head = nullptr;
    // now how many items are in the list?
    std::cout << head << '\n';
    std::cout << z << '\n';