Consider this in add_node_in_bet():
struct node *p = (*tail)->next;
new->data = info;
if (p == *tail)
{
new->next = (*tail)->next;
(*tail)->next = new;
(*tail) = new;
}
else if(p==(*tail)->next)
{
new->next = p;
(*tail)->next= new;
}
The first test, (p == *tail), is true for a list with a single node. If there are more than one node, then the condition (p==(*tail)->next) will be true for sure since it is set in the declaration of p.
This way the code never enters the while loop.
Note also that
tail can be NULL
- if
tail is not NULL, *tail can be NULL
more about the code
Sure you can write this way, but is very fragile, close to impossible to reuse, and not a good abstraction for your data.
See:
struct node
{
struct node *next;
int data;
};
This is the data structure in use. It is fragile. And it is not even a list.
Consider this code:
int main(void)
{
struct node *tail = NULL;
add_node_in_bet(&tail, 0, 2);
}
This will crash immediately. The first line is supposed to (implicitly) create a list. Implicit things are evil and can cost grades, hours and jobs.
add_node_in_bet() will crash since the first thing it does is access (*tail)->next...
an abstraction to a Linked List
A Linked List is a (possibly empty) collection of nodes. A node is not a List. A list is not a node. A node is not the data. Each node contains a data unit or points to one. Failure to represent this will only make things harder.
A circular linked list means only that the last element (node) points to the first.
A singly linked list is a list that has pointer to one side only. And is much more complicated to program than lists with pointers to both directions. Navigation is a mess, and in a circular list it is worse.
An example of such a List
typedef struct st_node
{
int data;
struct st_node* next;
} Node;
typedef struct
{ // single linked circular list?
// makes no difference
Node* head;
Node* tail;
size_t size;
} List;
Here we see a List and a Node. And the list has metadata: each List has inside it pointers and its size. It this saves much trouble: writing this way you can have an array of lists and manage all them at ease in the same code.
some functions and a complete C example
Note that the arguments of the functions are described in the <summary> comments in the code. But is simple stuff and a possible implementation code is also below.
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
typedef struct st_node
{
int data;
struct st_node* next;
} Node;
typedef struct
{ // single linked circular list
Node* head;
Node* tail;
size_t size;
} List;
/// <summary>
/// so_create() returns a pointer for a new list
/// so_delete() destroys a list and returns NULL
/// so_insert_back() inserts at the end
/// so_insert_front() inserts at the front
/// so_insert_multi_xxx() inserts the 'N' supplied
/// values at front or back
/// so_insert_pos() inserts at pos 'N', being 0 the
/// list head
/// so_print() shows the lsit contents. accepts an
/// optional message///
/// </summary>
///
List* so_create();
List* so_delete(List*);
int so_insert_back(const int data, List*);
int so_insert_front(const int data, List*);
int so_insert_multi_back(List*, const unsigned N, ...);
int so_insert_multi_front(List*, const unsigned N, ...);
int so_insert_pos(const int data, const size_t N, List*);
int so_print(List*, const char* msg);
main for a simple test
int main(void)
{
List* L = so_create();
so_insert_multi_back(L, 7, 69, 65, 19, 67, 68, 61, 64);
so_print(L, NULL);
so_insert_pos(0,2,L);
so_print(L, "\nafter insert 0 at pos 2\n");
so_delete(L);
return 0;
}
This 10-line program does the same the code in the original question tried to.
some definitions
Consider the list 1>2>3>4.
- Being it a circular list, the first element is
1, the last is 4, and the circular thing just means that 4 points back to 1.
- insert at the end means insert after
4
- insert at front means insert before
1
- insert at
x position means insert before the element x nodes away from the head.
- insert at 0 position means insert before the head
- since the list is circular with
size nodes we can use % modulus if pos x is greater than the size of the list
- insert at a given position should not change head or tail. But it is a design decision. In this example I will not change them. It is trivial anyway.
output of the example
#7 elements in circular list.
head/tail element: 69
69 65 19 67 68 61 64
[-----]
after insert 0 at pos 2
#8 elements in circular list.
head/tail element: 69
69 65 0 19 67 68 61 64
[-----]
the complete code
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
typedef struct st_node
{
int data;
struct st_node* next;
} Node;
typedef struct
{ // single linked circular list
Node* head;
Node* tail;
size_t size;
} List;
/// <summary>
/// so_create() returns a pointer for a new list
/// so_delete() destroys a list and returns NULL
/// so_insert_back() inserts at the end
/// so_insert_front() inserts at the front
/// so_insert_multi_xxx() inserts the 'N' supplied
/// values at front or back
/// so_insert_pos() inserts at pos 'N', being 0 the
/// list head
/// so_print() shows the lsit contents. accepts an
/// optional message///
/// </summary>
///
List* so_create();
List* so_delete(List*);
int so_insert_back(const int data, List*);
int so_insert_front(const int data, List*);
int so_insert_multi_back(List*, const unsigned N, ...);
int so_insert_multi_front(List*, const unsigned N, ...);
int so_insert_pos(const int data, const size_t N, List*);
int so_print(List*, const char* msg);
int main(void)
{
List* L = so_create();
so_insert_multi_back(L, 7, 69, 65, 19, 67, 68, 61, 64);
so_print(L, NULL);
so_insert_pos(0,2,L);
so_print(L, "\nafter insert 0 at pos 2\n");
so_delete(L);
return 0;
}
List* so_create()
{
List* one = malloc(sizeof(List));
if (one == NULL) return NULL; // error
one->size = 0;
one->head = NULL;
return one;
}
List* so_delete(List* del)
{
if (del == NULL) return NULL; // no list
for (size_t i = 0; i > del->size; i += 1)
{ // delete nodes, 1 by 1
Node* save = del->head->next;
free(del->head);
del->head = save;
};
free(del); // delete list
return NULL;
}
int so_insert_back(const int info, List* list)
{
if (list == NULL) return -1; // no list
Node* node = (Node*)malloc(sizeof(Node));
if (node == NULL) return -2; // no alloc
node->data = info;
if (list->size == 0)
{
node->next = node;
list->size = 1;
list->head = node;
list->tail = node;
return 0; // 1st node
};
node->next = list->head;
list->tail->next = node;
list->tail = node;
list->size += 1;
return 0;
}
int so_insert_front(const int info, List* list)
{
if (list == NULL) return -1; // no list
Node* node = (Node*)malloc(sizeof(Node));
if (node == NULL) return -2; // no alloc
node->data = info;
if (list->size == 0)
{
node->next = node;
list->size = 1;
list->head = node;
list->tail = node;
return 0; // 1st node
};
node->next = list->head;
list->tail->next = node;
list->head = node;
list->size += 1;
return 0;
}
int so_insert_multi_back(List* list, const unsigned N, ...)
{
va_list args;
va_start(args, N);
for (size_t i = 0; i < N; i += 1)
so_insert_back(va_arg(args, int), list);
va_end(args);
return 0;
}
int so_insert_multi_front(List* list, const unsigned N, ...)
{
va_list args;
va_start(args, N);
for (size_t i = 0; i < N; i += 1)
so_insert_front(va_arg(args, int), list);
va_end(args);
return 0;
}
int so_insert_pos(
const int el, const size_t pos, List* list)
{
if (pos == 0) return so_insert_back(el, list);
if (list->size == 0) return so_insert_front(el, list);
// list is not empty
size_t n = pos % list->size;
if (n == 0) return so_insert_back(el, list);
// create the new node
Node* node = (Node*)malloc(sizeof(Node));
if (node == NULL) return -1;
node->data = el; // data loaded
// navigate to position
Node* p = list->head;
for (size_t i = 0; i < n - 1; i += 1) p = p->next;
// insert 'node' after p
node->next = p->next;
p->next = node;
list->size += 1;
return 0; // 1st node
}
int so_print(List* list, const char* msg)
{
if (list == NULL)
{
printf("No valid list!\n");
return 0;
}
if (list->size == 0)
{
printf("list is empty\n");
return 0;
}
if (msg != NULL) printf("%s", msg);
printf(
"\
#%llu elements in circular list.\n\
head/tail element: %i\n ",
list->size, list->head->data);
Node* p = list->head;
for (size_t i = 0; i < list->size; i += 1)
{
printf("%d ", p->data);
p = p->next;
}
printf("\n[-----]\n");
return 0;
}
notes about the example code
Since we have now an abstraction of a List, all functions uses a simple List*. To access any list. This is an important distinction from the use of Node** as if it was a list, as in the original code. With no size and no pointers inside, Node** can be a container, but it needs to be built. It it is fragile and requires tons of additional pointers and counters. Obvious things like the actual number of nodes must be painfully computed each time if needed.
Having pointers to head and tail makes life easier. Since we have only forward pointers, keeping the tail pointer in the list is a good investment for a single pointer for each list. My opinion, anyway.
multi-insert functions
int so_insert_multi_back(List*, const unsigned N, ...);
int so_insert_multi_front(List*, const unsigned N, ...);
These are here just for convenience, but note that elements will be inserted in the supplied order, so this code
int main(void)
{
List* L = so_create();
so_insert_multi_back(L, 5, 1, 2, 3, 4, 5);
so_print(
L, "\nafter multi_insert_back 1,2,3,4,5\n");
so_insert_multi_front(L, 3, 0, -1, -2);
so_print(
L, "\nafter multi_insert_front 0,-1,-2\n");
so_delete(L);
return 0;
}
shows
after multi_insert_back 1,2,3,4,5
#5 elements in circular list.
head/tail element: 1
1 2 3 4 5
[-----]
after multi_insert_front 0,-1,-2
#8 elements in circular list.
head/tail element: -2
-2 -1 0 1 2 3 4 5
[-----]
Since when elements are multi-inserted the order is reversed... This is just my decision and the code is included, anyway ;)
Note the convenience of so_print() accepting an optional message, so in test it is easy to identify situations without an army of printf calls.
I did only a minimal testing, in a single computer. HIH
https://stackoverflow.com/questions/77681742/linked-list-doesnt-add-node-properly
nodestructure? And how big is a pointer to anodestructure? Tip: Use the variable itself when getting the size for how much to allocate, as instruct node *new = malloc(sizeof *new);snake_casewithcamelCase.