Failing to build a 'queue-like' structure - problems with struct declarations? - c

Hey guys. This is a very simple question, I'm sure, but I'm getting myself tangled up in C references/pointers as per usual. I am trying to build a... sort-of-queue, using a sort-of-linked list. Basically, I have a struct which has contents and a pointer to the next element. I also have a pointer to the first and last elements. I then have a loop that will be building the 'sort-of-queue'. My problem is that either my logic is failing and I'm not initialising the queue right, or my knowledge of C structs is failing (which is very probable) and I'm ending up just creating one struct and constantly referring to it.
My test code is as follows:
#include <stdio.h>
struct test {
int contents;
struct test *next;
};
main() {
struct test *first = NULL;
struct test *last = NULL;
int i;
for (i = 0; i < 2; i++) {
struct test tmp;
if (first == NULL) {
first = &tmp;
last = &tmp;
} else {
last->next = &tmp;
last = &tmp;
}
tmp.x = i;
tmp.next = NULL;
}
while (first != NULL) {
printf("%d\n", first->x);
first = first->next;
}
return 0;
}
Running this, I get the output that first seems to point to a test struct that has the value of '1' as it's 'x' variable - so not the initial one like I intended. So, am I failing at logic here, or am I failing at understanding how to declare new separate structs in a loop? Or maybe both? I'm very tired... >_<.
Thanks.

The problem you have is that you are taking the address of a temporary variable, tmp, and assigning it to a pointer which lives much longer than teh temporary, first and last. After every iteration of the loop the temporary is gone and continuing to access it via first and last results in undefined behavior.
You need to create a value on the heap in order to build up the list like so (error checking omitted for brevity)
struct test* tmp = malloc(sizeof(struct test));
Later though you'll need to go through and free all of the allocated nodes.

Related

linked list traversal goes infinitely

I'm trying to implement my own version of malloc() function in c.
I decided to keep track of my allocated blocks using a linked list of meta-data objects that would store some information about the allocated chunk and and place it right before the chunk.
Now long story short while debugging I came across the fact that my linked list is behaving very strangely.
here's a piece of the code to help understanding the problem.
typedef struct meta_data
{
size_t size;
int free;
struct meta_data* next;
}meta_data;
meta_data* global_base;
void *mymalloc(size_t size)
{
if(size > 0)
{
meta_data block_meta;
void* pointer = sbrk(size + block_size);
block_meta.size = size;
block_meta.next = NULL;
block_meta.free = 0;
if(!global_base) //first allocation
{
global_base = &block_meta;
}
else
{
block_meta.next = global_base;
}
return pointer;
}
else
{
return NULL;
}
}
I wrote this code which I assume will append a new item to the tail of my global_base (linked list) every time I call mymalloc(<some_size>);
however when I tried to debug and make sure that my linked list is in order by calling mymalloc() couple of times and check is my linked list is being populated properly
void printList()
{
meta_data * node = global_base;
while (node->next != NULL)
{
printf("%zu", node->size);
printf(" -> ");
node = node->next;
}
printf(" \n ");
}
int main()
{
mymalloc(10);
mymalloc(8);
mymalloc(4);
printList();
return 0;
}
I expected my output to be
10 -> 8 -> 4 however it was 4 -> 4 -> 4 -> 4 -> 4 ..... and goes into an infinite loop
any idea where am I going wrong with this code.
I'm a little new to programming with C so my only guess is that I'm making use of reference & and pointer * improperly.
furthermore I have seen tones of code where the assignment of struct's attribute is happening with the use of -> but I could only use . to make it (could this be the problem anyhow)?
help is appreciated thanks guys
There are multiple problems with your approach:
the meta_data block_meta; is a local variable. You cannot use that to link the blocks. Local variables are reclaimed when the function exits. You should use global memory retrieved from the system with sbrk, pointed to by pointer.
the print loop is incorrect: you should write while (node != NULL) to print all blocks.
Your code has dozens of issues which I will address.
Now the problem with your code, in fact the biggest issue with it is that the myalloc function doesn't create a new block_meta node. It just declares a block_meta variable (which will end up on the stack and even worse is a recipe for disastrous bugs I believe the infinite loop is a result of this). You should you use the sbrk function to create a meta_data node before doing anything like so:
...
meta_data *block_meta = sbrk(sizeof(meta_data));
block_meta->size = size;
block_meta->next = NULL;
block_meta->free = 0;
if(!global_base)
{
global_base = block_meta;
}
The myalloc function checks to see if global_base has been assigned, that is if there is a root node in the linked list. If there is one it should simply tell the current variable to link itself to the global_base that is point it's next variable at global_base, this is a big error. Firstly, the global_base is the root node and it makes no sense to tell the current node to link itself to the root of the linked list, it's supposed to link itself to the next node not the root. Secondly, the reference to the previous node is lost which means it's not a linked list anymore. A proper solution would be saving the current node in a variable using a static variable which prevents it from getting lost when the myalloc function exits:
...
static meta_data *curr;
then right before returning the pointer to the newly allocated block you add this line to hold the node that had been newly created:
...
curr = block_meta;
return pointer;
now return to the erring else statement and change that to to ensure that the previous node points to the current node:
...
else
{
curr->next = block_meta;
}

Freeing the temp node when adding to a linkedlist

I have a function called addMod that, when called, adds a node to a certain index of an array of Module struct LinkedLists called modules contained within a System struct. A Module struct has a string field, two int fields, and a pointer to the next Module, the first three fields being initialized according to arguments provided in addMod. addMod roughly looks like this:
int addMod(System *system, const char *text, int num1, int num2, int index) {
Module *temp = malloc(sizeof(Module));
Module *current;
temp->next = NULL;
if ([any of the constructors are invalid]) return 0;
temp->text = malloc(strlen(text)+1);
strcpy(temp->text, text);
temp->num1 = num1; temp->num2 = num2;
if (!system->modules[index]) {
system->modules[index] = temp; //If there are no modules in the LinkedList at the given index, makes the head = temp.
}
else {
if (system->compare(temp, system->modules[index]) <= 0) { //compare is a func pointer field of system that compares two Modules to see in what order they should be. Here, we check if temp should become the head of modules[index].
temp->next = system->modules[index]; //Assigns the current head as the module following temp.
system->modules[index] = temp; //Makes temp the current head.
}
else {
current = system->modules[index];
while (current->next && system->compare(temp, current->next) > 0) { //While current isn't the last node in the LinkedList and temp comes after the node after current
current = current->next;
}
temp->next = current->next; //Adds temp in between current and current->next.
current->next = temp;
}
}
return 1;
}
All of the above works as expected, except when printing the contents of system, the console indicates there's a memory leak that I'm assuming is because I fail to properly free temp based on what valgrind tells me. My problem is not knowing where to free it- it seems anywhere I put it causes a segfault after printing the contents. From my understanding, I have to make sure that no other variables are depending upon the value being held by temp, but I can't seem to find a way to do that considering every possible ending of my if statement leads to assigning temp to a node within modules. Putting free(temp) between the logic and return 1 also yields a segfault, I'm assuming because I often malloc temp again when calling addMod multiple times in succession.
In summary, to add a new node to a LinkedList that may or may not be populated, in which this new node may be inserted in any arbitrary position in the LinkedList, I have to allocate memory to a temporary node so that I can insert it later. Where do I free this allocated memory once I have successfully inserted the node?
Assuming your management of a System instance is sound (a big assumption, since I cannot see that code), you have giant hole in the memory allocation of temp with a subsequent hard return 0 in the condition where the "constructor" check fails. More to the point:
Module *temp = malloc(sizeof(Module)); // memory allocated here...
Module *current;
temp->next = NULL;
if ([any of the constructors are invalid])
return 0; // and leaked here.
It may be as simple as swapping the check around. Obviously other code that is supposed to free the dynamic allocations should be considered and evaluated as well.
A Simpler Approach
The node addition code is complicated and it need not be. In the end all you should really care about is finding the place where your new node resides.
If the slot in the table is empty, its the first node in that list.
IF the slot in the table is NOT empty, find the sorted location and insert it there.
Both of those can be accomplished with a single while-loop by using a pointer-to-pointer, where said entity hold the address of the pointer that will hold the new node in either of the cases above, and as a bonus, surgical insertion is literally two assignments.
It's done like this. Note that most of this code is just making the Module object safely. The actual insertion is only a single while-loop and some pointer assignments. It assumes the table in System initially contains NULL entries:
int addMod(System *system, const char *text, int num1, int num2, int index)
{
// allocate new node here
Module *temp = malloc(sizeof *temp);
if (temp == NULL)
{
perror("Failed to allocate new Module");
return 0;
}
size_t len = strlen(text);
temp->text = malloc(len + 1);
if (temp->text == NULL)
{
perror("Failed to allocate module name");
free(temp);
return 0;
}
// finish copying member data
memcpy(temp->text, text, len);
temp->text[len] = 0;
temp->num1 = num1;
temp->num2 = num2;
// now find where it belongs, and set next appropriately
Module **pp = system->modules + index;
while (*pp && system->compare(temp, *pp) <= 0)
pp = &(*pp)->next;
temp->next = *pp;
*pp = temp;
return 1;
}
Understand this is from deriving what I think your System type looks like, as it was never presented:
typedef struct System
{
Module *modules[MAX_MODULES];
int (*compare)(const Module* lhs, const Module *rhs);
} System;
I'm fairly confident it is similar to this. Of course, you'll have to adapt if it isn't. I suggest you review this and step through it in a debugger. There is no substitute for watching it live.
Best of luck.

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;
};

C - Expanding an array of structs with pointers already existing inside it

I have an array of structs that is declared as such
typedef struct bucket{
char * value;
char * key;
}BUCKET;
typedef struct item{
struct bucket * data;
struct item * next;
struct item * prev;
}ITEM;
typedef struct base{
struct item * first;
}BASE;
typedef BASE *SPACE;
It works perfectly for everything that I had to do with it. Basically I have to do an implementation of a hashmap in C. I managed to do it, but I am completely stuck on this one task. I need to make the hashmap resizable by the user.
If I want a hashmap of size 5, I do so:
SPACE *hashmap = malloc(sizeof(SPACE *) * 5);
and it works perfectly for the purpose of the program.
However, if I try to resize it using the following block of code:
void expandHashspace(SPACE *hashmap){
printf("Please enter how large you want the hashspace to be.\n");
printf("Enter a number between %d and 100. Enter any other number to exit.\n>",hashSpaceSize);
int temp = 0;
scanf("%d",&temp);
if(temp>100 || temp<hashSpaceSize){
printf("Exiting...\n");
}
else {
SPACE *nw = NULL;
nw = realloc(hashmap, sizeof(SPACE *) * temp);
hashmap = nw;
hashSpaceSize = temp;
printf("Your hashspace is now %d rows long.\n", hashSpaceSize);
}
}
It also works properly. However, when I go to utilise the hashmap itself, it ends up with a segmentation fault. Or SIGSEGV Signal 11.
For example, I have the following display function.
void displayHashspace(SPACE *hashmap){
printf("\n");
int j = 0;
for(int i = 0; i < hashSpaceSize && hashmap; i++){
BASE *linkedList = hashmap[i];
if(linkedList) {
ITEM *node = linkedList->first;
printf("\n[HASH %d]\n", i);
while (node) {
printf("\t[BUCKET %d]\n\t[VALUE] : %s\n\t[KEY] : %s\n\n",j, node->data->value, node->data->key);
node = node->next;
j++;
etc...
Using CLion's debugging, I realised this:
Let's say the hashmap size is 3. That would mean that only hashmap[0-2] exist.
If I resize the hashmap to, let's say 10, it allows me to resize.
However, while displaying, the address of hashmap[3] is really weird.
Whereas every other address is pretty long, with almost 8 digits or more, the address of hashmap[3] is always 0x21.
After this, once it reaches ITEM *node = linkedList->first; with linkedList being hashmap[3], the segmentation fault occurs.
Here's another example. Here's my saving function:
void saveHash(SPACE *hashmap){
FILE *f = fopen("hashmap.hsh","w");
fprintf(f,"%d\n",hashSpaceSize);
for(int i = 0; i < hashSpaceSize;i++){
if(hashmap[i]){
ITEM *save = hashmap[i]->first;
do{
fprintf(f,"---\n%s\n%s\n",save->data->value,save->data->key);
save = save->next;
}while(save);
etc...
Here, the story is different. It can only reach hashmap[0] before crashing after the resizing. Using the debugger, I found that somehow, the save, which is set to hashmap[0]->first (which normally works before expanding), has a BUCKET whose VALUE variable is suddenly set to NULL for some reason, hence the crash.
I tried setting every "new" BASE after expansion to NULL, but the save function still breaks after using expandHashspace().
What am I doing wrong?
Reallocating memory to hashmap wasn't working because hashmap was being a local variable in that method. Meaning everything just became a confusing nightmare.
Returning the hashmap itself instead of returning nothing solved every problem.

Array of structs, unable to check if entry is NULL

Im toying with creating my own hash table data structure, but I have hit an unexpected problem, which I can't solve and haven't found a satisfying solution for it yet.
See, I have this linked list struct
struct Link
{
int v;
struct Link* next;
}
and then inside the hash table structure I want keep track of an array linked lists, like this:
struct Link** entries;
What I run into is, that for this to work, I first have to initialize the array like this:
entries = malloc(sizeof(struct Link*) * N);
for (i = 0; i < N; i++)
entries[i] = malloc(sizeof(struct Link));
What I want is not having to do the for loop that initializes the structs, because that's not how linked lists work, I want to leave entries[x] blank until it actually gets assigned a value.
If I don't do the for loop this happens:
if (entries[x] != NULL) /* true, the array is initialized */
entries[x]->v = value; /* SEGFAULT, there is no struct initialized */
That if statement 'should' return false if I haven't assigned a Link struct to it yet, but it doesn't.
One way of solving this problem is to just initialize all the first links of the linked list with that for loop and then checking for the value, but that's not what I want.
So does anyone know a way to solve this the way I want it to?
You can use calloc rather than malloc while allocating entries like:
entries = calloc(sizeof(struct Link*), N);
Well, you can't. Uninitialized pointers are not necessarily NULL.
You will need to initialize the pointer array after you allocate space for it with malloc(). You can use another for-loop like:
for (i = 0; i < N; i++)
{
entries[i] = NULL;
}
or simply use calloc() to allocate the array. This will set all elements to 0.
entries = calloc(N, sizeof(struct Link*));
Afterwards you need to allocate the elements as needed:
if (entries[x] == NULL)
{
entries[x] = malloc(sizeof(struct Link));
}
entries[x]->v = value;

Resources