I am trying to write a simple graph for storing sizes of created tables. I am new to C so I am sorry if I miss some simple pointer operations.
My graph is containing previous and next pointers, where first next will point to the last node and serve as a iterator to grab next->next. I don't want to store any more information then those two pointers and size of the graph, so one can reach the nodes only trough the iteration of next pointer.
Here is a minimal reproducible example of my code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct GC {
uint data;
uint size;
struct GC *next;
struct GC *prev;
};
void build_gc_root(struct GC **self) {
// Build root node
printf("Allocating gc.\n");
struct GC *tmp = malloc(sizeof *tmp);
tmp->size = 0;
tmp->data = 666;
tmp->prev = tmp;
tmp->next = tmp;
printf("Saving allocation of gc.\n");
(*self) = tmp;
}
void gc_clean(struct GC **self) {
uint _;
printf("Cleaning the gc table.\n");
for (_ = 0; _ < (*self)->size + 1; ++_) {
printf("Free node %hd.\n", (*self)->next->next->data);
free((*self)->next->next);
++(*self)->next;
}
printf("Free last node %hd.\n", (*self)->next->data);
free((*self)->next);
}
void gc_store(struct GC **self, uint data) {
printf("Storing GC value node.\n");
(*self)->next = malloc(sizeof *(*self)->next);
(*self)->next->data = data;
(*self)->next->prev = (*self)->prev;
(*self)->next->next = (*self);
(*self)->prev = (*self)->next;
++(*self)->size;
}
typedef struct {
void (*clean)();
void (*get_size)(uint size);
void (*print_table)();
struct GC *gc;
}__controller;
__controller controller;
void controller_clean() {
gc_clean(&controller.gc);
}
void controller_get_size(uint size) {
gc_store(&controller.gc, size);
}
void controller_print_table() {
uint index;
printf("GC catch: \n");
for (index = 0; index < controller.gc->size + 1; ++index) {
printf(" %hd\n", controller.gc->next->next->data);
--controller.gc->next;
}
putchar('\n');
}
__controller controller = {
controller_clean, controller_get_size, controller_print_table
};
int main (void) {
build_gc_root(&controller.gc);
controller.get_size(1);
controller.get_size(2);
controller.get_size(3);
controller.print_table();
controller.clean();
return 0;
}
After running this, the controller.print_table() method will output only two first node's data and end up with segmentation fault.
Basically you are creating a stack that is linked via prev pointer. The next is rather useless and you should think about removing it.
When it comes to printing or deleting the content, you have used wrong mechanism to iterate over your list.
Operators ++ and -- used on pointers move them to the next/previous consecutive element in memory. This is only valid within an array which you do not use.
Instead you need to follow the links in your structure to reach the next element.
This would look like this:
void gc_clean(struct GC** self) {
uint _;
printf("Cleaning the gc table.\n");
struct GC* gc = (*self)->prev;
for (_ = 0; _ < (*self)->size; ++_) {
printf("Free node %hd.\n", gc->data);
struct GC* tmp = gc;
gc = gc->prev;
free(tmp);
}
printf("Free last node %hd.\n", gc->data);
free(gc);
}
void controller_print_table() {
uint index;
printf("GC catch: \n");
struct GC* gc = controller.gc->prev;
for (index = 0; index < controller.gc->size + 1; ++index) {
printf(" %hd\n", gc->data);
gc = gc->prev;
}
putchar('\n');
}
Please note that this also removes the initial element from your stack which you added during creation. Also the pointers of your controller.gc are not valid any more. You need to initialize again after cleaning the list.
As a side node, you should think about naming.
The function to add an element to your stack, is called get_size which is confusing to put it mildly...
I post the working code bellow in case someone wanna see a working stack:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
// Garbage collector stack
struct GC {
uint data;
uint size;
struct GC *prev;
};
void build_gc_root(struct GC **self) {
// Build root node
printf("Allocating gc.\n");
struct GC *tmp = malloc(sizeof *tmp);
tmp->size = 0;
tmp->data = 666;
tmp->prev = tmp;
printf("Saving allocation of gc.\n");
(*self) = tmp;
}
void gc_clean(struct GC **self) {
uint _;
printf("Cleaning the gc table.\n");
struct GC *gc = (*self)->prev;
for (_ = 0; _ < (*self)->size; ++_) {
printf("Free node %hd\n", gc->data);
struct GC* tmp = gc;
gc = gc->prev;
free(tmp);
}
printf("Free the last node %hd\n", gc->data);
free(gc);
}
void gc_store(struct GC **self, uint data) {
printf("Storing GC value node.\n");
struct GC *node;
node = malloc(sizeof *(*self)->prev);
node->data = data;
node->prev = (*self)->prev;
(*self)->prev = node;
++(*self)->size;
}
void gc_print(struct GC *self) {
uint index;
struct GC *gc = self->prev`;
printf("GC catch: ");
for (index = 0; index < self->size + 1; ++index) {
printf(" %hd", gc->data);
gc = gc->prev;
}
putchar('\n');
}
typedef struct {
void (*clean)();
void (*get_size)(uint size);
void (*print_table)();
struct GC *gc;
}__controller;
__controller controller;
void controller_clean() {
gc_clean(&controller.gc);
}
void controller_get_size(uint size) {
gc_store(&controller.gc, size);
}
void controller_print_table() {
gc_print(controller.gc);
}
__controller controller = {
controller_clean, controller_get_size, controller_print_table
};
int main (void) {
build_gc_root(&controller.gc);
controller.get_size(1);
controller.get_size(2);
controller.get_size(3);
controller.print_table();
controller.clean();
return 0;
}
Related
i'm trying to adapt the example code from this site https://www.codesdope.com/blog/article/making-a-queue-using-linked-list-in-c/
but i can't get it to work properly, when changing the node datatype to somesting "more complex"
thought it would be simple but when i call display it outputs all "0"
// #include "include/main.h"
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#define QUEUE_SIZE 30
typedef float dataArray[20];
struct node
{
dataArray *data;
struct node *next;
};
typedef struct node node;
struct queue
{
int count;
node *front;
node *rear;
};
typedef struct queue queue;
void initialize(queue *q);
int is_empty(queue *q);
void enqueue(queue *q, dataArray value);
void dequeue(queue *q, dataArray *outputArray);
void display(node *head);
float random_float();
void initialize(queue *q)
{
q->count = 0;
q->front = NULL;
q->rear = NULL;
}
int is_empty(queue *q)
{
return (q->rear == NULL);
}
void enqueue(queue *q, dataArray value)
{
if (q->count == QUEUE_SIZE)
{
dataArray tmp;
dequeue(q, &tmp);
free(tmp);
}
node *tmp;
tmp = malloc(sizeof(node));
printf("[enqueue] sizeof node %d\r\n", (uint) sizeof(node));
// memcpy(tmp->data, value, sizeof(value));
tmp->data = &value;
tmp->next = NULL;
printf(" contents of value in enqueue\n");
for (int i = 0; i < 20; i++)
{
printf("%f\n", value[i]);
}
printf(" contents of tmp-node in enqueue\n");
for (int i = 0; i < 20; i++)
{
printf("%f\n", tmp->data[i]);
}
if (!is_empty(q))
{
q->rear->next = tmp;
q->rear = tmp;
}
else
{
q->front = q->rear = tmp;
}
q->count++;
}
void dequeue(queue *q, dataArray *outputArray)
{
node *tmp;
outputArray = q->front->data;
printf("dequeue output before freeing memory\r\n===========\r\n");
for (int i = 0; i < 20; i++)
{
printf("%f\n", outputArray[i]);
}
printf("[dequeue] size %d - %d\r\n", (uint) sizeof(q->front->data), (uint) sizeof(q->front->data[0]));
tmp = q->front;
q->front = q->front->next;
q->count--;
free(tmp);
}
void display(node *head)
{
if (head == NULL)
{
printf("NULL\r\n");
}
else
{
for (int i = 0; i < 20; i++)
{
printf("%f\n", head->data[i]);
}
display(head->next);
}
}
float random_float()
{
srand((unsigned int)time(NULL));
float a = 50.0;
return ((float)rand() / (float)(RAND_MAX)) * a;
}
int main()
{
queue *q;
q = malloc(sizeof(queue));
initialize(q);
srand((unsigned int)time(NULL));
dataArray tmp;
for (int i = 0; i < 20; i++)
{
tmp[i] = random_float();
}
printf("display dataArray before fill\r\n===========\r\n");
for (int i = 0; i < 20; i++)
{
printf("%f\n", tmp[i]);
}
enqueue(q, tmp);
printf("Queue display after init and fill\r\n===========\r\n");
// THIS WILL PRINT 0 <<<<--------------------------------------- what it shouldn't
display(q->front);
printf("Queue before dequeue\r\n===========\r\n");
printf("Queue #1 element count: %d\r\n", q->count);
// Nächsten Queue Eintrag holen
dataArray *queData = malloc(sizeof(dataArray));
dequeue(q, queData);
printf("Queue after dequeue\r\n===========\r\n");
printf("Queue #1 element count: %d\r\n", q->count);
for (int i = 0; i < 20; i++)
{
printf("%f\n", queData[i]);
}
return 0;
}
does anybody know what i'm doing wrong?
(also first i wanted to give enqueue the pointer of my temp-array so it don't has to copy it but that worked even worse)
EDIT
this is the version where i'm passing the pointer of the array to enqueue, but in this version even within enqueue the output is not correct (only for the first value of the array)
// #include "include/main.h"
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#define QUEUE_SIZE 30
typedef float dataArray[20];
struct node
{
dataArray *data;
struct node *next;
};
typedef struct node node;
struct queue
{
int count;
node *front;
node *rear;
};
typedef struct queue queue;
void initialize(queue *q);
int is_empty(queue *q);
void enqueue(queue *q, dataArray *value);
void dequeue(queue *q, dataArray *outputArray);
void display(node *head);
float random_float();
void initialize(queue *q)
{
q->count = 0;
q->front = NULL;
q->rear = NULL;
}
int is_empty(queue *q)
{
return (q->rear == NULL);
}
void enqueue(queue *q, dataArray *value)
{
if (q->count == QUEUE_SIZE)
{
dataArray tmp;
dequeue(q, &tmp);
free(tmp);
}
printf(" contents of value in enqueue\n");
for (int i = 0; i < 20; i++)
{
printf("%f\n", *value[i]);
}
node *tmp;
tmp = malloc(sizeof(node));
printf("[enqueue] sizeof node %d\r\n", (uint) sizeof(node));
// memcpy(tmp->data, &value, sizeof(value));
tmp->data = value;
tmp->next = NULL;
printf(" contents of tmp-node in enqueue\n");
for (int i = 0; i < 20; i++)
{
printf("%f\n", tmp->data[i]);
}
if (!is_empty(q))
{
q->rear->next = tmp;
q->rear = tmp;
}
else
{
q->front = q->rear = tmp;
}
q->count++;
}
void dequeue(queue *q, dataArray *outputArray)
{
node *tmp;
outputArray = q->front->data;
printf("dequeue output before freeing memory\r\n===========\r\n");
for (int i = 0; i < 20; i++)
{
printf("%f\n", outputArray[i]);
}
printf("[dequeue] size %d - %d\r\n", (uint) sizeof(q->front->data), (uint) sizeof(q->front->data[0]));
tmp = q->front;
q->front = q->front->next;
q->count--;
free(tmp);
}
void display(node *head)
{
if (head == NULL)
{
printf("NULL\r\n");
}
else
{
for (int i = 0; i < 20; i++)
{
printf("%f\n", head->data[i]);
}
display(head->next);
}
}
float random_float()
{
srand((unsigned int)time(NULL));
float a = 50.0;
return ((float)rand() / (float)(RAND_MAX)) * a;
}
int main()
{
queue *q;
q = malloc(sizeof(queue));
initialize(q);
srand((unsigned int)time(NULL));
dataArray tmp;
for (int i = 0; i < 20; i++)
{
tmp[i] = random_float();
}
printf("display dataArray before fill\r\n===========\r\n");
for (int i = 0; i < 20; i++)
{
printf("%f\n", tmp[i]);
}
enqueue(q, &tmp);
printf("Queue display after init and fill\r\n===========\r\n");
display(q->front);
printf("Queue before dequeue\r\n===========\r\n");
printf("Queue #1 element count: %d\r\n", q->count);
// Nächsten Queue Eintrag holen
dataArray *queData = malloc(sizeof(dataArray));
dequeue(q, queData);
printf("Queue after dequeue\r\n===========\r\n");
printf("Queue #1 element count: %d\r\n", q->count);
for (int i = 0; i < 20; i++)
{
printf("%f\n", queData[i]);
}
return 0;
}
"yes that would explain why the first example won't work, but the second SHOULD" -- No, no it won't.
First, enable Full Warnings in your compiler1. Now try and compile your second example. Notice the problems in all of your attempted printf() statements where %f expects a double arguments, but you are passing float * (a pointer to float). Why aren't there more descriptive errors? What do the comments under your question say to remove? (hint: your typedef is masking errors)
So go to your second example and comment the typedef, e.g.
// typedef float dataArray[20];
Why was that causing problems? Look at your parameters, e.g.
void enqueue(queue *q, dataArray *value) { ...
What is your dataArray *value? It's float *value[20]; What's that? It is float *[20], an array of 20 pointers to float. When you assign tmp->data = value; what are you assigning? C18 - 6.3.2.1 Lvalues, arrays, and function designators(p3) tells you. value is converted to a pointer to it's first element (a pointer-to-pointer-to float, e.g. float**). What is tmp->data? It is an array of pointers to float and it is not an lvalue -- see 6.3.2.1(p1). You cannot assign to an array, but your typedef is masking the problem.
Now remove your typedef (throughout your code) and try again, e.g.:
void enqueue(queue *q, float *value[20])
{
if (q->count == QUEUE_SIZE)
{
float tmp[20];
dequeue(q, &tmp);
// free(tmp); /* you can't free what's not allocated */
}
printf(" contents of value in enqueue\n");
for (int i = 0; i < 20; i++)
{
printf("%f\n", *value[i]);
}
node *tmp;
tmp = malloc(sizeof(node));
printf("[enqueue] sizeof node %d\r\n", (uint) sizeof(node));
// memcpy(tmp->data, &value, sizeof(value));
tmp->data = value;
tmp->next = NULL;
...
(you had memcpy(), you were closer there but your types are messed up)
It doesn't work. Now the compiler easily sees the attempt to assign to a non-lvalue and provides a proper diagnostic:
source.c:65:15: error: assignment to expression with array type
tmp->data = value;
^
That problem runs throughout your code. Where does the mismatch begin? In main(). Look at what you are trying to pass to enqueue(), e.g.
int main()
{
...
dataArray tmp; /* e.g. float tmp[20] */
...
enqueue(q, &tmp);
...
}
When you take the address of tmp the type is float (*)[20], a pointer-to-array-of float[20], not an array-of-pointers-to float (20 of them). A pointer-to-array has type float (*)[20] while the array-of-pointers* is float *[20] -- completely different types and pointer arithmetic is completely different between the two.
So when you pass &tmp with enqueue(q, &tmp); (where tmp is passed as value to void enqueue(queue *q, dataArray *value)) and then do value[i] in your first loop, for values of i > 0, you invoke undefined behavior reading beyond the end of the array (passed as a pointer-to-array, value[1] is one past the end of your array tmp in main() of float[20])
Can You Make It Work?
Yes, just fix the types and remove the second call to srand() in random_float() that is messing up your fill and fix your memcpy() call. (there are numerous other small issues, memory not validated and memory not freed, etc..) Carrying the removal of your typedef through the rest of your code and making the types consistent, you would end up with something like:
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define QUEUE_SIZE 30
#define ASZ 20
// typedef float dataArray[20];
typedef unsigned uint;
struct node
{
float data[ASZ];
struct node *next;
};
typedef struct node node;
struct queue
{
int count;
node *front;
node *rear;
};
typedef struct queue queue;
void initialize(queue *q);
int is_empty(queue *q);
void enqueue(queue *q, float *value);
void dequeue(queue *q, float *outputArray);
void display(node *head);
float random_float();
void initialize(queue *q)
{
q->count = 0;
q->front = NULL;
q->rear = NULL;
}
int is_empty(queue *q)
{
return (q->rear == NULL);
}
void enqueue(queue *q, float *value)
{
if (q->count == QUEUE_SIZE)
{
float tmp[ASZ];
dequeue(q, tmp);
// free(tmp);
}
printf(" contents of value in enqueue\n");
for (int i = 0; i < ASZ; i++)
{
printf("%f\n", value[i]);
}
node *tmp;
tmp = malloc(sizeof(node));
printf("[enqueue] sizeof node %d\r\n", (uint) sizeof(node));
// memcpy(tmp->data, &value, sizeof(value));
memcpy (tmp->data, value, ASZ * sizeof *value);
tmp->next = NULL;
printf(" contents of tmp-node in enqueue\n");
for (int i = 0; i < ASZ; i++)
{
printf("%f\n", tmp->data[i]);
}
if (!is_empty(q))
{
q->rear->next = tmp;
q->rear = tmp;
}
else
{
q->front = q->rear = tmp;
}
q->count++;
}
void dequeue(queue *q, float *outputArray)
{
node *tmp;
outputArray = q->front->data;
printf("dequeue output before freeing memory\r\n===========\r\n");
for (int i = 0; i < ASZ; i++)
{
printf("%f\n", outputArray[i]);
}
printf("[dequeue] size %d - %d\r\n", (uint) sizeof(q->front->data),
(uint) sizeof(q->front->data[0]));
tmp = q->front;
q->front = q->front->next;
q->count--;
free(tmp);
}
void display(node *head)
{
if (head == NULL)
{
printf("NULL\r\n");
}
else
{
for (int i = 0; i < ASZ; i++)
{
printf("%f\n", head->data[i]);
}
display(head->next);
}
}
float random_float()
{
float a = 50.0;
return ((float)rand() / (float)(RAND_MAX)) * a;
}
int main()
{
queue *q;
q = malloc(sizeof(queue));
initialize(q);
srand((unsigned int)time(NULL));
float tmp[ASZ];
for (int i = 0; i < ASZ; i++)
{
tmp[i] = random_float();
}
printf("display dataArray before fill\r\n===========\r\n");
for (int i = 0; i < ASZ; i++)
{
printf("%f\n", tmp[i]);
}
enqueue(q, tmp);
printf("Queue display after init and fill\r\n===========\r\n");
display(q->front);
printf("Queue before dequeue\r\n===========\r\n");
printf("Queue #1 element count: %d\r\n", q->count);
// Nächsten Queue Eintrag holen
float *queData = malloc(sizeof(float[ASZ]));
dequeue(q, queData);
printf("Queue after dequeue\r\n===========\r\n");
printf("Queue #1 element count: %d\r\n", q->count);
for (int i = 0; i < ASZ; i++)
{
printf("%f\n", queData[i]);
}
return 0;
}
Example Use/Output
Now your second example compiles without warning and, to the extent the logic is correct, produces reasonable output (whether it is correct is left to you to determine), e.g.
$ ./bin/llqueue
display dataArray before fill
===========
5.864568
15.599927
1.622677
6.834927
18.953743
43.170605
37.056793
21.260620
44.568668
41.187069
43.298340
8.273252
13.328741
33.927246
32.561504
14.666824
20.176983
30.163134
21.064775
8.606315
contents of value in enqueue
5.864568
15.599927
1.622677
6.834927
18.953743
43.170605
37.056793
21.260620
44.568668
41.187069
43.298340
8.273252
13.328741
33.927246
32.561504
14.666824
20.176983
30.163134
21.064775
8.606315
[enqueue] sizeof node 88
contents of tmp-node in enqueue
5.864568
15.599927
1.622677
6.834927
18.953743
43.170605
37.056793
21.260620
44.568668
41.187069
43.298340
8.273252
13.328741
33.927246
32.561504
14.666824
20.176983
30.163134
21.064775
8.606315
Queue display after init and fill
===========
5.864568
15.599927
1.622677
6.834927
18.953743
43.170605
37.056793
21.260620
44.568668
41.187069
43.298340
8.273252
13.328741
33.927246
32.561504
14.666824
20.176983
30.163134
21.064775
8.606315
NULL
Queue before dequeue
===========
Queue #1 element count: 1
dequeue output before freeing memory
===========
5.864568
15.599927
1.622677
6.834927
18.953743
43.170605
37.056793
21.260620
44.568668
41.187069
43.298340
8.273252
13.328741
33.927246
32.561504
14.666824
20.176983
30.163134
21.064775
8.606315
[dequeue] size 80 - 4
Queue after dequeue
===========
Queue #1 element count: 0
-0.001425
-0.001425
-0.001425
-0.001425
-0.001425
-0.001425
-0.001425
-0.001425
-0.001425
-0.001425
-0.001425
-0.001425
-0.001425
-0.001425
-0.001425
-0.001425
-0.001425
-0.001425
-0.001425
-0.001425
Note: this isn't an effort to correct ALL of the issues with your code. This merely gets you back on track from your typedef fiasco. While there are times where typedef'ing arrays makes sense (for experienced kernel developers, etc..), it's not something to do lightly for the reasons explained below. Pointers should not be typedef'ed either. See Is it a good idea to typedef pointers?.
footnotes:
Always compile with warnings enabled, and do not accept code until it compiles without warning. To enable warnings add -Wall -Wextra -pedantic to your gcc/clang compile string (also consider adding -Wshadow to warn on shadowed variables). Turn on -Werror to treat all warnings as errors. For VS (cl.exe on windows), use /W3. All other compilers will have similar options. Read and understand each warning -- then go fix it. The warnings will identify any problems, and the exact line on which they occur. You can learn a lot by listening to what your compiler is telling you.
I'm trying to copy a list into another list with memcpy, but I'm getting a segmentation fault everytime I try to access the value I copied.
I've already tried moving pointers around, but the problem still occurs.
create_list creates a new head node for the list and returns it. Here is some of the code:
/* The n variable shows the number of elements in the list for the head */
struct list {
union{
void *data;
struct {
unsigned num;
List *end;
};
};
List *node;
};
List *
create_list()
{
List *head;
head = malloc(sizeof(List));
if (head == NULL)
return NULL;
head->num = 0;
head->end = NULL;
head->node = NULL;
return head;
}
int
cpy_list(List *l1, List **l2)
{
List *iter;
void *data_aux;
*l2 = create_list();
iter = l1->node;
while (iter != l1->end) {
memcpy(&data_aux, iter->data, sizeof(iter->data));
//printf("data_aux: %s\n", data_aux);
insert_list(*l2, data_aux, NULL);
//printf("iter->data: %s\n", iter->data);
iter = iter->node;
}
return 1;
}
void
print_list(List *head)
{
List *iter;
iter = head->node;
printf("[");
while (iter != head->end) {
printf("\"%s\",", iter->data);
iter = iter->node;
}
printf("\"%s\"", iter->data);
printf("]");
printf("\n");
}
int
main(int argc, char *argv[])
{
List *l1, *l2;
char *str[] = {"Test0", "Test1", "Test2", "Test3", "Test4"};
void *data_aux;
l1 = create_list();
for (int k = 0; k < 5; k++) {
insert_list(l1 ,str[k], NULL);
}
printf("l1: ");
print_list(l1);
cpy_list(l1, &l2);
print_list(l2);
return 0;
}
memcpy(&data_aux, iter->data, sizeof(iter->data));
I'll assume this is a typo problem (let me know if it is not and you intended to use it like that). &data_aux will return the address of variable data_aux, not the address pointed by data_aux. This code is likely causing a stack overflow as you are probably
writing data beyond the boundaries of the local variable data_aux (which have the size of a pointer - 4 bytes on x86 or 8 bytes on x64). If iter->data have a significant size you will corrupt the stack and have undefined behavior.
What you probably want is to allocate a buffer to be pointed by data_aux. Something like:
data_aux = malloc(sizeof(iter->data));
And then pass data_aux instead of &data_aux in your call to memcpy.
I'm trying to build an arraylist in C with the following code
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
// Declaration of ArrayList structure
typedef struct ArrayList {
int length, capacity;
int *items;
} ArrayList;
// Create a new ArrayList
ArrayList *newList() {
int *items = malloc(4 * sizeof(int));
ArrayList *list = malloc(sizeof(ArrayList));
list->length = 0;
list->capacity = 4;
list->items = items;
return list;
}
// Check and expand list if neccessary
void check(ArrayList *list) {
printf("Check called (%d, %d)\n", list->length, list->capacity);
if (list->length >= list->capacity) {
printf("Expanding\n");
list->capacity = list->capacity * 2;
printf("Reallocating\n");
list->items = realloc(list->items, list->capacity);
if (list->items == NULL) {
printf("realloc failed\n");
exit(1);
}
}
}
// Add a value to the ArrayList
void add(ArrayList *list, int n) {
check(list);
list->items[list->length] = n;
list->length++;
}
// Print the list
void printList(ArrayList *list) {
for (int i=0; i<list->length; i++) {
if (i > 0) printf(", ");
printf("%d", list->items[i]);
}
printf("\n");
}
int main () {
ArrayList *list = newList();
for (int i = 0; i < 20; i++) {
add(list, i);
}
printList(list);
}
When the array is full, the check function is called as it should be. However, the second time the check function is called, the program failes on the call to realloc giving the following error:
*** Error in `./test': realloc(): invalid next size: 0x0000000001d3c010 ***
Aborted (core dumped)
where the size varies every time the program is run.
I have read that this error is caused by a corrupt heap, which is normally caused by pointers going wrong somewhere. However, I cannot see where the problem lies in this example. Any help would be appreciated.
you are reallocating the list->items. realloc() function has 2 parameter
first one is void pointer ,this point to the memory block that previously allocated,and second parameter works for how many bytes have to reallocate.
in your code you added only the capacity...bt it is not .u have to add size of the int with capacity ...cause it only takes (size ) int byte ...
then it works fine
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
// Declaration of ArrayList structure
typedef struct ArrayList {
int length, capacity;
int *items;
} ArrayList;
int i;
// Create a new ArrayList
ArrayList *newList() {
int *items = malloc(4 * sizeof(int));
ArrayList *list = malloc(sizeof(ArrayList));
list->length = 0;
list->capacity = 4;
list->items = items;
return list;
}
// Check and expand list if neccessary
void check(ArrayList *list) {
printf("Check called (%d, %d)\n", list->length, list->capacity);
if (list->length >= list->capacity) {
printf("Expanding\n");
list->capacity = list->capacity * 2;
printf("Reallocating\n");
list->items = realloc(list->items, list->capacity * sizeof(int));
if (list->items == NULL) {
printf("realloc failed\n");
exit(1);
}
}
}
// Add a value to the ArrayList
void add(ArrayList *list, int n) {
check(list);
list->items[list->length] = n;
list->length++;
}
// Print the list
void printList(ArrayList *list) {
for (i=0; i<list->length; i++) {
if (i > 0) printf(", ");
printf("%d", list->items[i]);
}
printf("\n");
}
int main () {
ArrayList *list = newList();
for ( i = 0; i < 20; i++) {
add(list, i);
}
printList(list);
}
I'm trying to implement sequence_insert_at using the add_to_front function here
Everything before
typedef struct sequence *Sequence;
is pasted from another c file.
void sequence_insert_at(Sequence s, int pos, int item)
{
struct node* temp = s->lst;
for(; pos > 0; --pos)
{
temp = temp->rest;
}
add_to_front(&temp, item);
++s->length;
if(!temp->rest)
{
s->end = temp;
}
//s->lst = temp;
}
I don't know why I keep getting a runtime error. if I clone s->lst and traverse the clone, I'm not modifying the pointer to the node in s, but if I change temp, s->lst should have the reflected changes since the nodes are all linked still. Any ideas as to how to fix this? I tried creating another node that is one before the temp after traversal, and then setting it->rest = temp, but that failed as well.
following mistakes a could spot but only so far to get the main function run
new_sequence does not initialize anything in Sequence it creates. lst is not initialized when you access it in sequence_insert_at
struct node* temp = s->lst;
here how it should look like
Sequence new_sequence()
{
Sequence s = malloc(sizeof(struct sequence));
if(!s)
{
printf("Out of memory. Can't allocate s\n");
exit(EXIT_FAILURE);
}
s->lst = malloc(sizeof(struct node));
if(! s->lst) {
printf("Out of memory. Can't allocate lst\n");
}
s->lst->rest = NULL;
s->length = 0;
return s;
}
also s->lst->rest has to be set to NULL, this is what tells that the list has no more elements an not end witch turns obsolete.
struct sequence
{
struct node* lst;
int length;
};
You should be passing the sequence itself to your functions not a pointer to some internal data in the sequence.
add_to_front(&temp, item);
Your sequence_insert_at function should be the one that can handle any position not add_to_front() so it is easier to call with the position 0 from add_to_front() and your having the the hole work done in one function, not a half here and a half there.
void sequence_insert_at(Sequence s, int pos, int item)
{
if(s && pos <= s->length) {
print_sequence(s);
struct node *newnode = malloc(sizeof(struct node));
if (newnode == NULL) {
printf("ERROR! add_to_front ran out of memory!\n");
exit(EXIT_FAILURE);
}
newnode->first = item;
struct node* temp = s->lst;
struct node* prv = NULL;
for(int i = 0; i < pos; i++) {
printf("skip %d\n", temp->first);
prv = temp;
temp = temp->rest;
}
newnode->rest = temp;
if(pos == 0) {
printf("insert as first\n");
s->lst = newnode;
} else {
printf("insert before %d\n", temp->first);
prv->rest = newnode;
}
++s->length;
}
}
and in add_to_front only one statement is needed
void add_to_front(Sequence s, int item) {
sequence_insert_at(s, 0, item);
}
as for inserting at the back of the list
void add_to_back(Sequence s, int item) {
sequence_insert_at(s, s->length, item);
}
A small test with the main function
void print_sequence(Sequence s)
{
struct node* temp = s->lst;
for(int i = 0; i < s->length; temp = temp->rest) {
printf("%d ", temp->first);
i++;
}
printf("\n");
}
int main()
{
Sequence derp = new_sequence();
sequence_insert_at(derp, 0, 14);
add_to_front(derp, 16);
sequence_insert_at(derp, 0, 17);
sequence_insert_at(derp, 2, 15);
add_to_back(derp, 13);
print_sequence(derp);
delete_sequence(derp);
return 0;
}
output is:
17 16 15 14 13
You'll have to go trough the other functions and fix them.
Finally i should note that variable names you have choosen are little bit confusing if not misleading, i would name them this way
typedef struct node {
int data; /* the data that a node holds */
struct node* next; /* the pointer to the next node */
} Node_t;
typedef struct sequence {
struct node* head; /* head or first element of the sequence/list */
int length; /* length is ok but size is better */
} Sequence_t;
This program is a work in progress. It is going to simulate a multiprocessor and I am programming it with producer-consumer sync.
Few problems here:
- My pending_request counter starts 1 lower than it should and goes down to -1. It should stop at 0.
- My remove_queue function also keeps removing one over. It will remove until the list is blank, but it doesn't recognize the list is empty. Then if I run remove_queue one more time, then it recognizes the list empty. SAMPLE OUTPUT AT THE BOTTOM at http://tinyurl.com/3ftytol
#include <stdio.h>
#include <pthread.h>
#include <time.h>
#include <stdlib.h>
typedef struct pr_struct{
int owner;
int burst_time;
struct pr_struct *next_prcmd;
} prcmd_t;
static prcmd_t *pr_head = NULL;
static prcmd_t *pr_tail = NULL;
static int pending_request = 0;
static pthread_mutex_t prmutex = PTHREAD_MUTEX_INITIALIZER;
int add_queue(prcmd_t *node)
{ pthread_mutex_lock(&prmutex);
//code
prcmd_t *curNode = pr_head;
if(pr_head == NULL) { pr_head = node; return;}
while(curNode->next_prcmd)
{
curNode = curNode->next_prcmd;
}
curNode->next_prcmd = node;
//
pending_request++;
pthread_mutex_unlock(&prmutex);
return(0);
}
int remove_queue(prcmd_t **node)
{
pthread_mutex_lock(&prmutex);
if(pr_head == NULL)
{
//your code
printf("Queue is empty\n");
//
pthread_mutex_unlock(&prmutex);
return(-1);
}
else
{
//your code
prcmd_t *tempNode; tempNode = (prcmd_t*)malloc(sizeof(prcmd_t));
tempNode = *node;
*node = tempNode->next_prcmd;
free(tempNode);
//
pending_request--;
pthread_mutex_unlock(&prmutex);
return(0);
}
}
int get_number_request(void)
{ return pending_request; }
void display_list(prcmd_t *node)
{
if (pr_head == NULL)
{
printf("List is empty!\n\n");
}
printf("-----------\n");
while(node)
{
printf("%i %i\n", node->owner,node->burst_time);
node = node->next_prcmd;
}
int r = get_number_request();
printf("Pending requests: %i\n", r);
}
int main()
{
int i=0;
int length = 4;
prcmd_t *pr[length];
for(i =0;i<length;i++)
{
pr[i] = (prcmd_t*)malloc(sizeof(prcmd_t));
pr[i]->owner = i+1;
pr[i]->burst_time = i + 2;
add_queue(pr[i]);
}
display_list(pr_head);
remove_queue(&pr_head);
display_list(pr_head);
remove_queue(&pr_head);
display_list(pr_head);
remove_queue(&pr_head);
display_list(pr_head);
remove_queue(&pr_head);
display_list(pr_head);
remove_queue(&pr_head);
display_list(pr_head);
}
some things (although maybe not all):
There is no need for a tail pointer if the list is not doubly linked, because there is no way to go from tail to head (no previous pointer)
Why do you malloc in your remove queue?
*node = prHead;
prHead = prHead->next_prcmd;
--pending_request;
in add_queue you have to node->next_prcmd = NULL; otherwwise you will never know the end.
again, some things, but maybe not all...
Mario