Reputation: 23
I am trying to create a dynamic array of simple linked lists. The MAX_SIZE is 5. I would like to insert a new node, for example the character 'c', at (link[5]). For every new character, i would like to allocate a new entry at the bottom of the array.
When i execute my code, I get this output above. I think there is a problem with realloc
[0] a b NULL
[1] c NULL
[2] f e d NULL
[3] NULL
[4] NULL
but the expected output is below:
[0] a b NULL
[1] c NULL
[2] f e d NULL
[3] NULL
[4] NULL
[5] c
My code is here
#include <stdio.h>
#include <stdlib.h>
typedef struct Node
{
char data;
struct Node *next;
} Node;
int push_front( Node **head, char data )
{
Node *new_node = malloc( sizeof( Node ) );
int success = new_node != NULL;
if ( success )
{
new_node->data = data;
new_node->next = *head;
*head = new_node;
}
return success;
}
void output( Node **head )
{
for( Node *current =*head; current != NULL && current->data != -1 ; current = current->next )
{
printf( "%c ", current->data );
}
printf( "%s", "NULL" );
}
void display( Node **set, int n )
{
for (int i = 0; i < n; i++){
printf("[%d] ", i);
output(&(set[i]));
printf("\n");
}
}
#define N 5
int main(void)
{
//Node * link[N] = { 0 };
Node **link;
link = calloc(N , sizeof (Node*));
for (int i = 0; i < N; i++){
link[i] = calloc(N , sizeof (Node));
link[i]->data = -1;
}
push_front( &link[0], 'b' );
push_front( &link[0], 'a' );
push_front( &link[1], 'c' );
push_front( &link[2], 'd' );
push_front( &link[2], 'e' );
push_front( &link[2], 'f' );
link = (Node **) realloc(link, (N + 1) * sizeof(Node*));
for(int i = 0; i < N + 1; i++){
link[i] = (Node *)realloc(link[i],(N + 1) * sizeof(Node));
}
push_front( &link[5], 'c' );
display(link, N);
return 0;
}
Upvotes: 0
Views: 148
Reputation: 12679
OP is doing some serious mistake while allocating memory to list and the accepted answer is not pointing out those mistakes. Hence, I am posting this answer.
I am trying to create a dynamic array of simple linked lists....
The way you are allocating memory, initially, you are creating array of array's of Node
type objects.
From calloc(): [emphasis added]
void* calloc( size_t num, size_t size );
Allocates memory for an array of num objects of size and initializes all bytes in the allocated storage to zero.
So, this loop:
for (int i = 0; i < N; i++){
link[i] = calloc(N , sizeof (Node));
link[i]->data = -1;
}
will end up allocating memory for an array of N
objects of size of Node
type to link[i]
pointer, where value of i
lies in [0, 5)
.
The in-memory view would be something like this:
(read `n` as `next` pointer
and `:` is separator between `data` and `next` member of a node)
---- ----------------------
link[0] | | -> |-1:n|0:n|0:n|0:n|0:n| <---- array of Node type objects
| | ----------------------
| |
---- ----------------------
link[1] | | -> |-1:n|0:n|0:n|0:n|0:n| <---- array of Node type objects
| | ----------------------
| |
---- ----------------------
link[2] | | -> |-1:n|0:n|0:n|0:n|0:n| <---- array of Node type objects
| | ----------------------
| |
---- ----------------------
link[3] | | -> |-1:n|0:n|0:n|0:n|0:n| <---- array of Node type objects
| | ----------------------
| |
---- ----------------------
link[4] | | -> |-1:n|0:n|0:n|0:n|0:n| <---- array of Node type objects
| | ----------------------
| |
----
After calling push_front()
on link
array elements, the in-memory view would be something like this:
(read `n` as `next` pointer
and `:` is separator between `data` and `next` member of a node)
---- ----- ----- ----------------------
link[0] | | -> |a:n| -> |b:n| -> |-1:n|0:n|0:n|0:n|0:n|
| | ----- ----- ----------------------
| |
---- ----- ----------------------
link[1] | | -> |c:n| -> |-1:n|0:n|0:n|0:n|0:n|
| | ----- ----------------------
| |
---- ----- ----- ----- ----------------------
link[2] | | -> |f:n| -> |e:n| -> |d:n| -> |-1:n|0:n|0:n|0:n|0:n|
| | ----- ----- ----- ----------------------
| |
---- ----------------------
link[3] | | -> |-1:n|0:n|0:n|0:n|0:n|
| | ----------------------
| |
---- ----------------------
link[4] | | -> |-1:n|0:n|0:n|0:n|0:n|
| | ----------------------
| |
----
Here:
link = (Node **) realloc(link, (N + 1) * sizeof(Node*));
the link
pointer will be reallocated the memory of new size N + 1
(assuming realloc()
was success).
and, this loop:
for(int i = 0; i < N + 1; i++){
link[i] = (Node *)realloc(link[i],(N + 1) * sizeof(Node));
}
will end up reallocating link[i]
pointer to memory of size N + 1
objects of Node
type. You don't need to realloc()
the link[i]
pointers. The link[i]
pointers are head of their respective linked list.
Now, you must be thinking - why printing of list giving expected output?
Because the reallocation is done by either:
a) expanding or contracting the existing area pointed to by ptr, if possible. The contents of the area remain unchanged up to the lesser of the new and old sizes. If the area is expanded, the contents of the new part of the array are undefined.
b) allocating a new memory block of size new_size bytes, copying memory area with size equal the lesser of the new and the old sizes, and freeing the old block.
So, link[i]
pointers were pointing to a memory of a Node
type object and realloc()
will maintain the content of this memory after reallocation.
After reallocation, the in-memory view would be something like this:
(read `n` as `next` pointer, x as some garbage value
and `:` is separator between `data` and `next` member of a node)
---- ------------------------- ----- ----------------------
link[0] | | -> |a:n|x:n|x:n|x:n|x:n|x:n| |-> |b:n| -> |-1:n|0:n|0:n|0:n|0:n|
| | ---|--------------------- | ----- ----------------------
| | \----------------------/
---- ------------------------- ----------------------
link[1] | | -> |c:n|x:n|x:n|x:n|x:n|x:n| |-> |-1:n|0:n|0:n|0:n|0:n|
| | ---|--------------------- | ----------------------
| | \----------------------/
---- ------------------------- ----- ----- ----------------------
link[2] | | -> |f:n|x:n|x:n|x:n|x:n|x:n| |-> |e:n| -> |d:n| -> |-1:n|0:n|0:n|0:n|0:n|
| | ---|--------------------- | ----- ----- ----------------------
| | \----------------------/
---- --------------------------
link[3] | | -> |-1:n|0:n|0:n|0:n|0:n|x:n|
| | --------------------------
| |
---- --------------------------
link[4] | | -> |-1:n|0:n|0:n|0:n|0:n|x:n|
| | --------------------------
| |
---- -------------------------
link[5] | | -> |x:n|x:n|x:n|x:n|x:n|x:n|
| | -------------------------
| |
----
You can see the next
pointers are preserved after reallocation and that's why when you are printing the list you are getting output as expected. I hope this giving you clear picture of mistakes you are making while allocating memory.
Now, after this statement
push_front( &link[5], 'c' );
the in-memory view of link[5]
would be something like this:
---- ----- -------------------------
link[5] | | -> |c|n| -> |x:n|x:n|x:n|x:n|x:n|x:n|
| | ----- -------------------------
| |
----
Note that your output()
may not work as expected while printing the link[5]
list because the output()
function for
loop works on condition current != NULL && current->data != -1
and neither realloc()
initialise memory to 0
(as the calloc()
does) and nor you are assigning -1
to list[5]->data
.
You don't need to allocate memory to link[i]
pointers. The push_front()
function is allocating the memory and creating a new node and adding that node in the array member list whose address passed as argument to push_front()
function. Also, you should take care of freeing the list once done with them i.e. free the dynamically allocated memory before exiting.
Putting these altogether:
#include <stdio.h>
#include <stdlib.h>
#define N 5
typedef struct Node {
char data;
struct Node *next;
} Node;
int push_front (Node **head, char data) {
Node *new_node = malloc (sizeof (Node));
int success = new_node != NULL;
if (success) {
new_node->data = data;
new_node->next = *head;
*head = new_node;
}
return success;
}
void output (Node **head) {
for (Node *current = *head; current != NULL; current = current->next) {
printf ("%c ", current->data);
}
printf ("%s", "NULL");
}
void display (Node **set, int n) {
for (int i = 0; i < n; i++) {
printf ("[%d] ", i);
output (&(set[i]));
printf ("\n");
}
}
Node ** reallocate_list (Node **list, size_t old_size, size_t new_size) {
if (old_size == new_size) {
return list;
}
Node **temp = realloc (list, new_size * sizeof(Node*));
if (temp == NULL) {
printf ("Failed to allocate memory.\n");
/* do cleanup stuff and handle the allocation failure
* the way you want. I am simply exiting on allocation failure.
*/
exit (EXIT_FAILURE);
}
if (old_size < new_size) {
/* Initialise the newly added elements in the array
*/
for(size_t i = old_size; i < new_size; i++){
list[i] = NULL;
}
}
return temp;
}
void free_list (Node **head) {
Node *current = *head;
while (current) {
Node *x = current->next;
free (current);
current = x;
}
*head = NULL;
}
void free_all_list (Node **list, size_t num_of_lists) {
for (size_t i = 0; i < num_of_lists; i++) {
free_list (&list[i]);
}
}
int main (void) {
Node **link;
link = calloc(N , sizeof (Node*));
if (link == NULL) {
printf ("Failed to allocate memory.\n");
exit (EXIT_FAILURE);
}
printf ("link array size : %d\n", N);
/* handle the push_front() return value
*/
push_front (&link[0], 'b');
push_front (&link[0], 'a');
push_front (&link[1], 'c');
push_front (&link[2], 'd');
push_front (&link[2], 'e');
push_front (&link[2], 'f');
display (link, N);
printf ("Reallocating link array, new size : %d\n", N + 1);
link = reallocate_list (link, N, N + 1);
if (link == NULL) {
printf ("Failed to allocate memory.\n");
exit (EXIT_FAILURE);
}
push_front (&link[5], 'c');
display (link, N + 1);
free_all_list (link, N + 1);
free (link);
return 0;
}
Output:
# ./a.out
link array size : 5
[0] a b NULL
[1] c NULL
[2] f e d NULL
[3] NULL
[4] NULL
Reallocating link array, new size : 6
[0] a b NULL
[1] c NULL
[2] f e d NULL
[3] NULL
[4] NULL
[5] c NULL
Upvotes: 1