Home > Mobile >  C code to pop element shows segmentation error when the length of the list is 1 but works perfectly
C code to pop element shows segmentation error when the length of the list is 1 but works perfectly

Time:06-14

C code to pop element shows segmentation error when the length of the list is 1. This is my code:

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

struct Node
{
    int data;
    struct Node* next;
};

// create a node with data as x
struct Node* create_node(int x) {
    struct Node* ptr = malloc(sizeof(struct Node)); //this means ptr is pointng towards Node, i.e ptr will change values of Node and space is allocated
    ptr->next = NULL;
    ptr->data = x;
    return ptr;
}

// delete the node at `ptr` and free its memory
void delete_node(struct Node* ptr) {
    free(ptr);
}
struct Node* PythonListHead = NULL;


// prints the list in space-separated format

void print_list(struct Node* head) {
    struct Node* cur = head;
while(cur) {
    printf("%d ", cur->data);
    cur = cur->next;
}
printf("\n");
}

//PythonListHead=create_node(10);

// Add an item to the end of the list
void append(int x) { //working perfectly
    struct Node *cursor=PythonListHead; //cursor points to whatever PythonListHead points to
    struct Node* a=create_node(x);
   if (PythonListHead==NULL){ 
       PythonListHead=create_node(x);
    }
   else{
       while(cursor->next!=NULL){
           cursor=cursor->next;
       }
       cursor->next=a;
   }
}
/ Return the number of elements in the list
int len() { //working perfectly
    // your code goes here
    struct Node* last=PythonListHead;
    int count=0;
    while (last->next!=NULL){
        count  ;
        last=last->next;
    }
    return count 1;
}


// Remove the item at the end of the list
void pop() { //working nominally (not working when length of the list is 1)
    if (len()==1){
        PythonListHead=NULL;
    }
    if (len()==2){
        delete_node(PythonListHead->next);
        PythonListHead->next=NULL;
    }
    else{
        struct Node* cursor=PythonListHead;
        int count=0;
        while (cursor){
            cursor=cursor->next;
            count  ;
            if (count==(len()-2)){
                delete_node(cursor->next);
                cursor->next=NULL;
            }
        }
    }
}
int main(int argc, char const *argv[])
{

int T; 
scanf("%d", &T);

char operation_type[20];
int indices[100];

while(T--) {
    scanf("%s", operation_type);

    if(strcmp(operation_type, "append") == 0) {
        int x;
        scanf("%d", &x);
        append(x);
    } 

    if(strcmp(operation_type, "pop") == 0) {
        pop();
    }

    if(strcmp(operation_type, "len") == 0) {
        int length = len();
        printf("%d\n", length);
    }
    if(strcmp(operation_type, "print") == 0) {
        print_list(PythonListHead);
    }
}
}

I have added functions to append elements and print the list. The first line of input is an integer which shows the number of commands we can enter. The output is as follows:

20
append 1
append 2
append 3
print
1 2 3
pop
print
1 2
pop
print
1
pop
Segmentation fault: 11

The interesting thing is, that when I comment out this part of pop():

if (len()!=1 || len()!=2){
        struct Node* cursor=PythonListHead;
        int count=0;
        while (cursor){
            cursor=cursor->next;
            count  ;
            if (count==(len()-2)){
                delete_node(cursor->next);
                cursor->next=NULL;
            }
        }
    }

...Or add a return statement:

if (len()==1){
        PythonListHead=NULL;
        return;
    }

The code works perfectly again for a list with length one. Why's this happening? It shouldn't go into the third block else{} at all, and even if it does it will terminate because the while condition will not be fulfilled. That's what I understand from my experience with Python, I don't fully understand how C compiles.

CodePudding user response:

Your len function has a bug. If you think of an empty list, then this:

int len() {
    struct Node* last=PythonListHead;
    while (last->next!=NULL) {         // if last == NULL, **boom**

... will dereference a NULL pointer. Fix:

int len() {
    int count = 0;
    for(struct Node* last = PythonListHead; last; last = last->next) {
        count  ;
    }
    return count;
}

Your append function leaks. It creates two nodes for the first element appended and only saves one of them. Fix:

void append(int x) {
    struct Node* a = create_node(x);
    if (PythonListHead == NULL) {
        PythonListHead = a;       // not create_node(x)
    } else {
        struct Node* cursor = PythonListHead;
        while (cursor->next != NULL) {
            cursor = cursor->next;
        }
        cursor->next = a;
    }
}

You could simplify append like this:

void append(int x) {
    struct Node** cursor = &PythonListHead;
    while(*cursor) cursor = &(*cursor)->next;
    *cursor = create_node(x);
}

That is, make cursor a pointer to a pointer and initialize it to point at PythonListHead. The while loop will go on until cursor points at a pointer pointing at NULL. Then assign the newly created Node* to the pointer cursor is pointing at. Example: If PythonListHead == NULL, the while loop will not do any loops (*cursor will be equal to NULL) so *cursor = create_node(x); will be the same as PythonListHead = create_node(x);.


Your pop function leaks. If the length is 1 you never delete the node. If len() starts out > 2 it will also continue to run after having called delete_node which makes the program have undefined behavior (and could therefore crash or do just about anything). It also has a logic flaw. If len() returns 1, it will enter the else of if (len()==2) {} else { ... }

if (len()==1) {
}
if (len()==2) {   // should have been `else if` here
} else {
    // it will enter here if len() == 1 too
}

You also call len() (which is an expensive function) multiple times. Fix:

void pop() {
    int length = len();                   // only one call to len()
    if (length == 1) {
        delete_node(PythonListHead);      // you forgot to delete here
        PythonListHead = NULL;
    } else if (length == 2) {             // else if
        delete_node(PythonListHead->next);
        PythonListHead->next = NULL;
    } else {                              // len == 0 or len > 2
        struct Node* cursor = PythonListHead;
        int count = 0;
        while (cursor) {
            cursor = cursor->next;
            count  ;
            if (count == (length - 2)) {
                delete_node(cursor->next);
                cursor->next = NULL;
                break;                    // last is deleted, must break out
            }
        }
    }
}

pop could however be simplified to neither have to use len() nor have special cases for 1 and 2:

void pop() {
    for(struct Node** cursor = &PythonListHead; *cursor; cursor = &(*cursor)->next) {
        if((*cursor)->next == NULL) {
            delete_node(*cursor);
            *cursor = NULL;
            break;
        }
    }
}

The pointer to pointer logic is here similar to that in the simplification of append above.

  • Related