简体   繁体   中英

Printing extra 0 and not removing front nodes in linked list implementation in C

My code has two separate (although likely connected) problems, one of which being that when I print out the linked list (regardless of if I push nodes to the front or to the back), it prints an additional 0 at the start of the linked list. I saw a similar post about this, but the implementation of the push method was different, as it didn't take head as an argument, so I've defined head in the main() method with:

struct node *head = NULL;

and my linked list instantiation looks like

struct node *temp, *ptr;
temp=(struct node*)malloc(sizeof(struct node));
if(temp==NULL) {
    exit(0);
}
temp->next=NULL;
if(head==NULL) {
    head=temp;
} else {
    ptr=head;
    while(ptr->next!=NULL) {
        ptr=ptr->next;
    }
    ptr->next=temp;
    ptr->data=NULL;
}

The issue I'm having is that I'm not sure whether or not the issue is in the print method, my push front method or my linked list instantiation.

The relevant code is:

case PUSH_FRONT: ;  // push onto front of list
    struct node *temp1;
    temp1=(struct node*)malloc(sizeof(struct node));
    if(temp1==NULL) {
        break;
    }
    temp1->next=NULL;
    temp1->data=val;
    if(head==NULL) {
        head=temp1;
    } else {
        temp1->next=head;
        head=temp1;
    }
    break;
case PRINT_LIST: ;  // print list
    printf("Elements: ");
    struct node *ptr4;
    ptr4=(struct node*)malloc(sizeof(struct node));

    if(head==NULL) {
        break;
    } else {
        ptr4=head;
        while(ptr4) {
            printf("%d",ptr4->data);
            printf(" ");
            ptr4=ptr4->next;
        }
        printf("\n");
        free(ptr4);
    }
    break;

My other issue (although I'm still convinced that this issue lies elsewhere) is that my pop front method does absolutely nothing, which I'm guessing has to do with the initial instantiation of the linked list. The relevant code is:

case POP_FRONT: ;   // remove from front of list
    // If list is empty, do nothing.
    struct node *ptr2;
    ptr2=(struct node *)malloc(sizeof(struct node));
    if(ptr2==NULL) {
        break;
    }
    if(head==NULL) {
        break;
    } else if(head->next==NULL) {
        ptr2=head;
        head=head->next;
        free(ptr2);
    }
    break;

You should post code someone could download and compile. And when needed a data file or a description of the problem. Code fragments are not good.

I believe that the data component of your list is just an int by looking at the code at the label PRINT_LIST

here goes what I think

  • a list is a collection of nodes. In java a list is even called a collection . In C++ lists are called containers . When you write code for a linked list as if it is a node you will have more work and less results.
  • as a direct consequence of using a node as a list you have 3 pointers here just to manage the list, in the instantiation code: head , temp and ptr , It is too much. Imagine if there where 3 lists...
  • a list with pointers only to one direction is harder to program and much less useful than one with pointers to next and previous nodes. If it is your decision may be you should reconsider. If the space of the additional pointer is not a concern, it is hard to justify not to use 2 pointers. Imagine a list for a library, a path,, a playlist, all the classic examples: it is very handful to be able to navigate in both directions.
  • the use of a switch() is uncommon. Why not just use functions?

back to your code

struct node *temp, *ptr;
temp=(struct node*)malloc(sizeof(struct node));
if(temp==NULL) {
    exit(0);
}
temp->next=NULL;
if(head==NULL) {
    head=temp;
} else {
    ptr=head;
    while(ptr->next!=NULL) {
        ptr=ptr->next;
    }
    ptr->next=temp;
    ptr->data=NULL;
}

here you write ptr->data = NULL; and we may think that data is a pointer, but in the list label you wrote

       ptr4=head;
        while(ptr4) {
            printf("%d",ptr4->data);
            printf(" ");
            ptr4=ptr4->next;
        }
        printf("\n");
        free(ptr4);

and data is just an int as you are using %d in the printf() . Well, the NULL in instantiation is, well, a zero.

And that NULL is the 0 you are complaining about.

This code seems to be much more complex and hard to read than it may need to be.

Let me show an alternative

about the declaration

You may write the node struct like

typedef struct _nd
{
    int         data;
    struct _nd* next;

}   node;

So you can use node in the declaration and not have to repeat struct at all times. Also it is useful to have a convention for typedef , like using first letter uppercase to help in readability

As I said before a list is a collection of nodes, it is NOT just a node --- with a pointer inside --- and each node has a payload, some data, usually a pointer. As an example consider

an alternate example of list structs


typedef struct _nda
{
    int          data;
    struct _nda* next;

}   Node;


struct _the_list
{
    char*     name;
    unsigned  size;
    unsigned  limit;
    Node*     head;
    Node*     tail;
};
typedef struct _the_list List;

Here the list is, well, List . And each list has head , tail , size , limit and even a name . I kept data as an int but is is not good: you really want it as a pointer, maybe (void*) .
Why? Because in this way you can reuse the code everywhere with no change.

How to use a List like this?

Using functions like these possible prototypes

List*      _create(const char*);
int        _define_max(List* l, const unsigned);
List*      _delete(List*);
int        _empty(List*);
int        _get_max(List*);
List*      _insert_begin(int, List*);
List*      _insert_end(int, List*);
int        _insert_your_way(List*, int(*)(int,int));
int        _print(List*);
int        _print_your_way(List*, int(*)(int,int));
List*      _remove(int, List*);
int        _size(List*);

I will post a running example below just to have something you can test or ask about case you need. But it is the usual. Only these two functions are less usual, but more useful:

int        _insert_your_way(List*, int(*F)(int,int));
int        _print_your_way(List*, int(*F)(int,int));

Here you can pass a function like in the qsort() function, and the node is inserted at the position, using the function F() to compare the nodes. The effect is that you can have the nodes inserted (or listed) in any order, with no change in the list code, just by providing different functions to the print or insert function. C does that, C++ does that, everybody does that, so we can too :)

code for instantiating such a list


List*      _create(const char* name)
{
    List* one = (List*)malloc(sizeof(List));
    one->name = (char*)malloc(1 + strlen(name));
    strcpy(one->name, name);
    one->size = 0;
    one->limit = 0;
    one->head = NULL;
    one->tail = NULL;
    return one;
};  // criar()

You may find that writing this way makes easier to maintain or read the code. And the List as a container is much more expressive: metadata about the list in included in the list. No need for ptr , head , temp , size or other controls hanging loose in main()

To create a list you can just write, like in the example

    List* first = _create("The First List");

inserting nodes at the beggining


List*      _insert_begin(int value, List* l)
{
    if (l == NULL) return l; //no list
    if ((l->limit > 0) && (l->size == l->limit)) return l; // full
    // ok: create a node and stuff data in
    Node* nd = (Node*)malloc(sizeof(Node));
    nd->data = value; // data comes in
    nd->next = l->head; // depois vem o que estava na frente
    l->head = nd; // nd fim
    l->size = l->size + 1;
    // if it is the first node
    if (l->size == 1)l->tail = nd;
    return l;
};

As I told you this is just a toy, an example. In practice you will use a void* in order to have a generic code. I am using an int as data here, as you did. To insert a few nodes in the list created above you just write

    // insert 6,7,8,9,10 at the end
    for(int i = 6; i<11; i+=1 ) _insert_end(i, first);

And you can have even an array of lists and all goes well. No external variables to look after. And each list has size updated, head, tail...

printing nodes

print is also easy and can be more expressive, since we have metadata with limits, size, head, tail and even the name for each list.

sample program

int main(void)
{
    List* first = _create("The First List");
    _print(first);
    _define_max(first,300);
    _print(first);

    // insert 5,4,3,2,1 at the beggining
    for(int i = 5; i>0; i-=1 ) _insert_begin(i, first);
    // list again
    _print(first);

    // insert 6,7,8,9,10 at the end
    for(int i = 6; i<11; i+=1 ) _insert_end(i, first);
    // list again
    _print(first);

    printf("empty(): %d size()= %d\n",
        _empty(first),
         _size(first) );

    first = _delete(first);
    _print(first);
    return 0;    
}

This code just

  • create a list and prints the nodes
  • set the optional limit to 300 nodes
  • list the nodes
  • insert 5,4,3,2,1 at the beginning
  • list the nodes
  • insert 6,7,8,9,10 at the tail
  • list the nodes
  • call a few functions on the list
  • deletes all data

output

List 'The First List' with 0 elements [MAX not defined yet]

List 'The First List' with 0 of 300 MAX elements

List 'The First List' with 5 of 300 MAX elements

    First:         1
     Last:         5

Elements

         1 
         2 
         3 
         4 
         5 

End of list


List 'The First List' with 10 of 300 MAX elements

    First:         1
     Last:        10

Elements

         1 
         2 
         3 
         4 
         5 
         6 
         7 
         8 
         9 
        10 

End of list

empty(): 0 size()= 10
Deleting 'The First List'
List not created!

the code (with almost no testing)

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

typedef struct _nda
{
    int          data;
    struct _nda* next;

}   Node;


struct _the_list
{
    char*     name;
    unsigned  size;
    unsigned  limit;
    Node*     head;
    Node*     tail;
};
typedef struct _the_list List;

List*      _create(const char*);
int        _define_max(List* l, const unsigned);
List*      _delete(List*);
int        _empty(List*);
int        _get_max(List*);
List*      _insert_begin(int, List*);
List*      _insert_end(int, List*);
int        _insert_your_way(List*, int(*)(void*));
int        _print(List*);
int        _print_your_way(List*, int(*)(void*));
List*      _remove(int, List*);
int        _size(List*);

int main(void)
{
    List* first = _create("The First List");
    _print(first);
    _define_max(first,300);
    _print(first);

    // insert 5,4,3,2,1 at the beggining
    for(int i = 5; i>0; i-=1 ) _insert_begin(i, first);
    // list again
    _print(first);

    // insert 6,7,8,9,10 at the end
    for(int i = 6; i<11; i+=1 ) _insert_end(i, first);
    // list again
    _print(first);

    printf("empty(): %d size()= %d\n",
        _empty(first),
         _size(first) );

    first = _delete(first);
    _print(first);
    return 0;    
}

List*      _create(const char* name)
{
    List* one = (List*)malloc(sizeof(List));
    one->name = (char*)malloc(1 + strlen(name));
    strcpy(one->name, name);
    one->size = 0;
    one->limit = 0;
    one->head = NULL;
    one->tail = NULL;
    return one;
};  // criar()

int        _define_max(List* l, const unsigned m)
{
    if (l == NULL) return -1;
    // new value can not be less than present size
    if (l->size > m) return -2;
    l->limit = m;
    return m;
};

List*      _delete(List* l)
{
    if (l == NULL) return NULL;
    printf("Deleting '%s'\n", l->name);
    free(l->name);
    if (l->size == 0)
    {
        free(l);
        return NULL; // empty
    };  // if()

    Node* node = l->head;
    do
    {
        Node* p = node->next; 
        free(node);
        node = p;
    } while (node != NULL);
    return NULL;
};

int        _empty(List* L)
{
    if (L == NULL) return -1;
    return (L->size == 0); 
};

int        _get_max(List* L)
{
    if (L == NULL) return -1;
    return (int)L->limit;
};

List*      _insert_begin(int value, List* l)
{
    if (l == NULL) return l; //no list
    if ((l->limit > 0) && (l->size == l->limit)) return l; // full
    // ok: create a node and stuff data in
    Node* nd = (Node*)malloc(sizeof(Node));
    nd->data = value; // data comes in
    nd->next = l->head; // depois vem o que estava na frente
    l->head = nd; // nd fim
    l->size = l->size + 1;
    // if it is the first node
    if (l->size == 1)l->tail = nd;
    return l;
};

List*      _insert_end(int value, List* l)
{
    if (l == NULL) return l;
    if ((l->limit > 0) && (l->size == l->limit)) return l; // full
    // ok: create a node and insert at the end
    Node* nd = (Node*)malloc(sizeof(Node));
    nd->data = value;
    // first one?
    if (l->size == 0)
    {
        l->head = nd;
        nd->next = NULL;
    }
    else
    {
        nd->next = NULL; // no one after this
        (l->tail)->next = nd;
    };  // if()
    l->tail = nd; // nd is tail now
    l->size = l->size + 1;
    // of this is the first node
    if (l->size == 1)l->head = nd;
    return l;
};

int        _insert_your_way(List* L, int(*F)(void*))
{
    return 0;
};

int        _print(List* l)
{
    if (l == NULL)
    {
        printf("List not created!\n");
        return -1;
    };

    if (l->limit > 0)
    {
        printf("\nList '%s' with %d of %d MAX elements\n",
            l->name,
            l->size,
            l->limit
            );
    }
    else
    {
        printf("\nList '%s' with %d elements [MAX not defined yet]\n",
            l->name,
            l->size
            );
    }
    if (l->size < 1) return 0;
    // assume data as just an int
    Node* p = l->head;
    printf("\n    First:%10d\n", l->head->data);
    printf("     Last:%10d\n", l->tail->data);
    printf("\nElements\n\n");
    do
    {
        printf("%10d \n", p->data);
        p = p->next;
    } while (p != NULL); 
    printf("\nEnd of list\n\n");
    return 0;
};  // _print()

int        _print_your_way(List* L, int(*F)(void*))
{
    return 0;
};

List*      _remove(int value, List* L)
{
    return NULL;
};

int        _size(List* L)
{
    if (L == NULL) return -1;
    return (int)L->size;
};

It was extracted for a larger example, for WIndows. Compiled just under gcc 9.3 on Ubuntu on Windows WSL

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