C - dynamically allocating a circular-buffer of structs within a struct - c

I am attempting to develop a dynamically-allocated circular-buffer in C using two structs. One holds detailed information and another is essentially used as a pointer from main to the circular-buffer structure (as there will be multiple arrays allocated at runtime).
Since it is a circular-buffer, I have a pointer "next" which points to the next item in the array (so last array index points to the first, etc.)
These are the two struct objects I have:
typedef struct {
int a;
int b;
struct1 *next; // pointer to next struct1 object in array
} struct1;
typedef struct {
struct1 *curr;
struct1 *start = NULL;
struct1 *end = NULL;
} struct2;
I then have my initialize function that is called from main to initiate a new circular-buffer.
This is the part where I am not entirely sure what to do.
#define minSize 10
struct2 * initialize()
{
struct2 **newBuf = malloc(sizeof(*newBuf));
newBuf->malloc(sizeof(*newBuf->quotes) * newBuf->minSize);
// set the start pointer
newBuf.curr[0] = newBuf->start;
newBuf.curr[0]->next = NULL;
for (int i = 1; i < minSize; i++)
{
struct1 *new = NULL;
newBuf.curr[i] = new; // make index i = NULL
// have the previous index point to the "next" current
if (i > 0)
newBuf.curr[i-1]->next = newBuf.curr[i];
}
// connect last index with first
newBuf.curr[minSize - 1]->next = newBuf.curr[0];
// set the end pointer
newBuf->end = newBuf->start;
return newBuf;
}
From searching I found this answer on how to initialize an array of structs within a struct by using malloc for initially allocating the space, but am confused how my code would line up since I have pointers to define start and end of the circular-buffer defined in struct2, as well as the next pointer as part of struct1.
Additionally, I've chosen to define ***newBuf* instead of **newBuf* as I was considering it as a pointer to pointers in a way (thinking about singly-linked lists). Though, please correct me if I am wrong.
I've done dynamically allocated circular-buffers in Java, but not C nor C++, so I am having a hard time figuring out the differences in how to initialize everything. I'm basically stuck at this mess and not sure where to go next.
Any help that can be given would be much appreciated!

The reason you're running into trouble is because you're trying to have the pointer to a pointer, rather than just using an ordinary pointer. You want to access the pointer that is contained at the address pointed to by the first pointer. As it stands you're trying to access a member that is outside of the memory space of the original pointer's address (which is only as large as an address). And then you're running into trouble because you aren't initializing your array 'curr' either. Another thing I did that doesn't really matter but helps you understand pointers is made your array a pointer- which is how arrays work in C. The array is simply the address of the first member of the array, and when you index into the array, it just adds an offset to that address = index * sizeof(yourstruct).
What you want is
typedef struct {
struct1 *curr;
struct1 *start = NULL;
struct1 *end = NULL;
} struct2;
#define minSize 10
struct2* initialize()
{
struct2 *newBuf = (struct2 *) malloc(sizeof(struct2));
newBuf->curr = (struct1 *) malloc(sizeof(struct1) * minSize);
// set the start pointer
newBuf.curr[0] = newBuf->start;
newBuf.curr[0]->next = NULL;
for (int i = 1; i < minSize; i++)
{
struct1 *new = (struct1 *) malloc(sizeof(struct1));
newBuf.curr[i] = new;
newBuf.curr[i-1]->next = newBuf.curr[i];
}
// connect last index with first
newBuf.curr[minSize - 1]->next = newBuf.curr[0];
// set the end pointer
newBuf->end = newBuf->start;
return newBuf;
}

Related

Pointers to structs in C vs pointers to arrays

Do pointers to structures in C work differently than other pointers?
For example in this code:
typedef struct node {
int data;
struct node *next;
} node;
void insert(node **head, int data) {
node *new_node = malloc(sizeof(node));
new_node->data = data;
new_node->next = *head;
*head = new_node;
}
int main() {
node *head = NULL;
insert(&head, 6);
Why do I have to use a pointer to a pointer and can't use the variable head in the insert function like in this example with arrays:
void moidify(int *arr) {
*arr = 3;
}
int main() {
int *array = malloc(8);
*array = 1;
*(array + 1) = 2;
moidify(array);
}
Here I don't have to pass &array to the function.
There is no difference. If you want to change the value of the variable you send in to function in such a way that the change is visible in the function that called function, you need to supply its address to function, which is what you do when taking the address of head.
In moidify(array) you send in a pointer to the first element in array which is why modifying the array data works. If you would like to modify the array variable itself (by making it potentially point somewhere else), you would have to take its address too. Example:
void moidify(int **arr) {
*arr = realloc(*arr, 128);
if(*arr == NULL) {
perror(__func__);
exit(1);
}
}
int main() {
int *array = malloc(8);
*array = 1;
*(array + 1) = 2;
moidify(&array);
}
You must understand how pointers works to get this one.
Here, the variable array is not properly speaking, an array. It's a pointer toward a memory space, of size 8 * sizeof(int). It contains only an address. From this address you can access the values of the array, you move using this address, to the rightfully memory space you want to fill or read.
Once that understood, when you call the moidify function, you are not passing the array. Nor the memory space. You are passing, the address of the memory space. The function gets a copy of the given address, in the argument variable int *arr.
Hence, you can use it the same way you use it from the main function.
If you wanted to change the address toward which the array variable would go, you would need to specify &array to the receiving function, which would then use an int ** argument variable.
Your example with struct is similar to this last part I just described, you want to change toward which address head is pointing, so, you need to give &head to the function. To get the address of head, and be able to modify the contained address.
You use an address, to access the memory space called head, to modify the address inside the memory space called head, which point toward another memory space, where your struct truly belongs.

I can alter a struct member from one location but not from the other

I am trying to implement a linked list in C - starting simple, with one list containing one node. However, I stumble upon some issues when trying to add data to the node. Here's my implementation thus far:
struct mylist_node {
int data;
};
struct mylist {
struct mylist_node *head_pt;
};
void mylist_init(struct mylist* l){
struct mylist_node head_node;
head_node.data = 5; //First try
l->head_pt = &head_node;
l->head_pt->data = 5; //Second try
};
And my main method:
int main()
{
struct mylist ml, *ml_pointer;
ml_pointer = &ml;
mylist_init(ml_pointer);
printf("%d\n", ml_pointer->head_pt->data);
ml_pointer->head_pt->data = 4;
printf("%d\n", ml_pointer->head_pt->data);
return 0;
}
This should print out
5
4
If my knowledge of pointers is correct. However, it prints out
0
4
As you can see I try to set the node data twice within the mylist_init method. Neither appears to be working - meanwhile, writing to and reading from it from my main method works just fine. What am I doing wrong?
In mylist_init, you're storing the address of a local variable in the struct pointed to by l. That variable goes out of scope when the function returns, so the memory it occupied is no longer valid, and thus the pointer that previously pointed to it now points to an invalid location. Returning the address of a local variable a dereferencing that address invokes undefined behavior.
Your function needs to allocate memory dynamically using malloc so the memory will still be valid when the function returns.
void mylist_init(struct mylist* l){
struct mylist_node *head_node = malloc(sizeof(*head_node));
l->head_pt = head_node;
l->head_pt->data = 5;
};
Also, don't forget to free the memory when you're done using it.
For starters, you have to allocate memory for your node, the way you were doing it, your node is a local variable on the stack which will likely get overwritten after the function exits.
void mylist_init(struct mylist* l)
{
struct mylist_node *head_node = (struct mylist_node *)malloc(sizeof(struct mylist_node));
head_node.data = 5; //First try
l->head_pt = head_node;
};

Declaring a pointer to struct creates a struct?

It seems to me like struct new_element *element = malloc(sizeof(*element)) creates a structure of type element, whereas I thought it would only create a pointer to it. The following code proves to me I'm wrong:
struct new_element
{
int i;
struct new_element *next;
};
int main(void)
{
struct new_element *element = malloc(sizeof(*element));
element->i = 5;
element->next = NULL;
printf("i = %d, next = %p\n", element->i, element->next);
}
Output:
i = 5, next = (nil);
element->i was given the value 5 and element->next was given the value NULL. Doesn't that mean that element points to a structure, which would mean that there is a structure that was created? I thought that malloc would only give a pointer the size needed in memory.
The variable element is a pointer. When you define it, that sets aside space for the pointer.
If you just did this:
struct new_element *element;
You've created a pointer. It just doesn't point anywhere.
When you then call malloc(sizeof(*element)), that sets aside space big enough for what element points to, i.e. an instance of struct new_element. You then point the variable element to this section of memory.
This syntax:
element->i = 5;
Is the same as:
(*element).i = 5;
It dereferences the pointer element, giving you a struct new_element, then you access the member i.
If you did this, as you suggested in the comments:
struct new_element *element = malloc(sizeof(element));
You're not allocating the proper amount of space. You're setting aside enough space for a struct new_element * instead of a struct new_element. If the struct is larger than a pointer to it (likely in this case, since it contains a pointer to its own type), then you end of writing past the end of the allocated memory when modifying one of the members. This invokes undefined behavior.

Allocating memory for a structure within a structure

typedef struct{
char id[15];
int count;
}hashtag;
typedef struct node{
hashtag *hashtag;
struct node*next;
}*link;
I'm writing a program to read hashtags from a sentence, and I want to store them in a list. I've already defined this two structures, and I can read and pass the hashtags to the function below but I need help allocating memory in order to copy the string to the list.
void check_insert(char newh[]){
link x;
//malloc to allocate memory for the string I want to copy
strcpy(x->hashtag->id, newh);
x->hashtag->count += 1;
head = insert_end(head, x->hashtag); //head is a global variable that points to the 1st element of the list
}
You should allocate and initialize the pointer x in check_insert, it is undefined behavior to dereference it and access its members without allocation first:
void check_insert(char newh[]){
link x = malloc(sizeof *x);
x->hashtag = malloc(sizeof *x->hashtag);
// strcpy(x->hashtag->id, newh); <-- UB if newh is larger than 14 in size
x->hashtag->id[0] = '\0';
strncat(x->hashtag->id, newh, sizeof(x->hashtag->id));
x->hashtag->count = 1;
head = insert_end(head, x->hashtag);
}

Global array for malloc

I am working on a project where I need to malloc some data. I am trying to reduce the heap peak of my program by changing around the main structure values I use. I am using a linked list. My struct is like:
struct myS{
int a,b;
float a,b;
struct myS *next;
};
I was thinking that instead of mallocing the struct pointer I would store it in a global array since I dont have much data. How would I do this ?
If you have upper bound for the number of elements you are going to need, you can create a global array [not dynamically allocated], let it be struct myS heap[], and an integer idx, initialized to 0. Once you allocate an element, you will need to increase idx, and attach this element to the requester.
Note - it is a good solution only if you are not expecting to delete elements [or you can afford to allocate each element only once].
If you do need delete, you will need to allocate an extra array that tells you which elements are currently in use, make idx circular [increase with idx = (idx + 1) % size], and check if each element is allocated before giving it, but as I say - it will probably be more time consuming!
code snap [not supporting deletes]:
struct myS heap[SIZE];
int idx = 0;
...
struct myS* allocate() {
return &(heap[idx++]);
}
Note: The above code snap is dangerous - it might overflow if you try to allocate more elements then you have in SIZE.
You could do something like this:
struct myS myArray[ARRAY_SIZE];
/* ... */
struct myS *head = &myArray[0];
head->next = &myArray[1];
head->next->next = &myArray[2];
/* etc... */
The array indexes used doesn't have to be sequential, e.g. head can be index 3 and head->next can be index 21
If you want to initialize the list to use all entries in the array at once, you could do it in a loop:
struct myS *node = head;
for (int i = 1; i < ARRAY_SIZE; i++)
{
node->next = &myArray[i];
node = &myArray[i];
}
node->next = NULL; /* Make sure the tail of the list doesn't have a 'next' pointer */

Resources