Accessing nested structures - c

I'm having some trouble accessing the checkups struct that is contained within dog. Since dog is contained within contained I assumed that I would just point to the checkups from dog from the container but that seems to be causing an error when trying to add a checkup date or replace one.
// used to create a linked list of containers, each contaning a "dog"
struct container {
struct dog *dog;
struct container *next;
} *list = NULL;
// used to hold dog information and linked list of "checkups"
struct dog {
char name[30];
char breed[30];
struct checkup *checkups;
};
// used to create a linked list of checkups containing "dates"
struct checkup {
char date[30];
struct checkup *next;
};
Here is the code where I try to add a new date to the checkups but when it tried to add it on my program crashes. Any direction on why this would be occurring would be greatly appreciated.
void add_checkup(char* name, char* date)
{
struct container *tempList = list;
struct checkup *tempCheck = (struct checkup *) malloc(sizeof(struct checkup));
while (tempList != NULL) {
if (strcmp(tempList->dog->name, name) == 0) {
strcpy(tempCheck->date, date);
strcpy(tempList->dog->checkups, tempCheck);
}
tempList->next;
}
}

Why are you using strcpy() to set the value of a struct checkup *? That attempts to copy the data pointed to by the source pointer to the array assumed to be pointed to by destination pointer, when in fact I see no particular reason to suppose that the destination pointer is even valid. Certainly there's no reason to suppose that the source data has the form of a null-terminated array of char, so your original code is badly wrong here.
Simple assignment is more likely to be correct:
tempList->dog->checkups = tempCheck;
Note, however, that that presents a serious risk of memory leakage because the previous value of tempList->dog->checkups is lost. Note also that it doesn't match the behavior implied by the add_ part of the function name -- it's more like set_. If you want to be able to maintain more than one checkup per dog then you would need to do more work.

Related

Is it possible to write a generic traverse function in C for different list structures so long as they contain the "next" field?

First time asking a question but I did look around Google and stackoverflow to see if someone has asked something similar before. In malloc, recasting and free, it looked like the OP asked something similar for example. But it was more complicated.
I was wondering whether it's possible to create a generic function for a list structure in C that traverses the list given that you know that the different types of structures will always have a "next" field.
For example, given these two list-type structures:
typedef struct _list1 {
int value;
list1 *next;
} list1;
typedef struct _list2 {
int value;
char *string;
list2 *next;
} list2;
Is it possible to create a generic void freeList((void *) list) function or something which looks something like the below? I am aware it's a simple thing to write both free functions for each individual list separately.
void freeList((void *) list) {
// Included this because the structs would have different sizes
// so I thought it would be possible to cast it in order to properly dereference the field.
if (sizeof *list == sizeof list1)
*list = (list1) list;
else if (sizeof *list == sizeof list2)
*list = (list2) list;
if (!list) return;
else {
free(list->next);
free(list);
}
}
So far, my experiments with the code shown above didn't fare well given that gcc would complain about dereferencing a void * pointer.
Making a heterogeneous list can be achieved by the use of a tagged union, or just a tag and casting:
struct list_item {
struct list_item *next;
enum datatype type;
void *contents;
};
or
struct list_item {
struct list_item *next;
enum datatype type;
union {
int some_int;
char some_char;
} contents;
};
Then while traversing the list you just have to verify the type stored in type before using the contents of the element.
This check:
if (sizeof *list == sizeof list1)
*list = (list1) list;
else if (sizeof *list == sizeof list2)
*list = (list2) list;
doesn't work because sizeof is a static construct: its value is defined at compilation time. You're just asking for the sizeof void.
is it possible to create a generic function for a list structure in C that traverses the list given that you know that the different types of structures will always have a "next" field.
Yes, as mentioned before; you must be careful that every structure starts with the "next" field; the two structures in your post should therefore be reordered like this:
typedef struct _list1 {
list1 *next;
int value;
} list1;
typedef struct _list2 {
list2 *next;
int value;
char *string;
} list2;
It is not clean code, because the compiler could reorder (and pad) the fields of the structure, but in general it should work.
Is it possible to create a generic void freeList((void) *list) function or something which looks something like...
This is possible if your structs do not refer malloced memory; or they do, but in a uniform (and known) way (note the first case is a sub-case of this last).
If the structs contain pointers pointing to memory that has to be freed, in fact, while freeing the struct the freeList() function should also free the referenced memory. A few solutions come to my mind:
1 - If all the different structs contain the same "pointers" layout, the routine can free those pointers in a uniform manner, knowing in advance what to do. In such scenario, one can also use pointer fields that are not used by all the structs, but only some.
2 - Every single instance of a struct could contain some helper field describing the pointer's layout. For example, just after the "next" field, another "mempntcnt" field could tell how many pointers (to be freed) follow the "next" field. Or, this "mempntcnt" could be passed as a parameter to freeList().
3 - This problem could be managed by a totally separated mechanism, outside the scope of freeList(). Much depends on the final usage: I mean, for a given (kind of) linked list, first call a routine that frees all the memory referenced by the list itself, then free the list by calling the common freeList(). After all, if different structs are needed, then different routines are used on them...
I hope I've been clear enough...
If you ensure that the next pointer is the first member of the struct then this is possible.
typedef struct list1 {
// next pointer must be first
struct list1 *next;
int value;
} list1;
typedef struct list2 {
// next pointer must be first
struct list2 *next;
int value;
char *string;
} list2;
void freeList(void *list) {
if (list) {
freeList(*(void**)list);
free(list);
}
}

Linked-list is confusing

I am stuck, as I don't understand what is this code doing:
struct node
{
int info; /* This is the value or the data
in the node, as I understand */
struct node *next; /* This looks like a pointer. But
what is it doing in real life? */
} *last; /* What is this and why is it
outside the body? What is this
thing doing? */
I know that when a node is created, it has a value, and it is pointing to some other node
but I don't understand the syntax.
Is this a better way of writing the code above?
Is there a simpler way of writing the same struct for better understanding?
In my lectures they presume that the student has understanding of what they teach.
Well, we can explain this to you, but we can't understand it for you.
Code snippet you've provided is definition of variable last, being pointer to newly defined structure type node. It can be written other way as:
typedef struct _node_t {
int info;
node_t *next;
} node_t;
node_t *last;
This way we define typedef, which is, say, alias of type definition to some short name — in this particular case, it aliases structure of two fields as the name node_t. Wherever you define something as being of type node_t, you tell compiler that you mean 'this should be aforementioned structure of two fields', and node_t *last means 'variable last should be pointer to node_t type'.
So, back to syntax:
struct foo {
int a;
float b;
void *c;
} bar, *baz;
means 'Define structure type foo, and make it contain three fields — integer a, float-point b and untyped pointer c, then make variable bar to be of this structure type, and make variable baz to point to this structure type'.
Now to pointer. What you see is called 'recursive definition', e.g. type mentions itself in it's own definition. They are okay, if language supports them (C does), but one could avoid recursive definitions in linked list node structure by specifying next node pointer to be just untyped:
struct node_t {
int info;
void *next;
};
This way you no longer reference node_t type from node_t type, but that adds you some inconveniences when using this type (you have to explicitly cast next to node_t type, like ((*node_t)(last->next))->info instead of just last->next->info).
If you feel you need additional reference, consider taking a look at interactive online tutorials, like http://www.learn-c.org/ (I'm not affiliated).
that is the simplest way to write a linked list node , but why name it last ? name it node instead , this makes it more understandable , but here's how it works.
when a linked list is first created it contains only the root node (the first node in a linked list) , when you add a node , you fill the info field with the data that node will hold (note that info may be any kind of data , character , string , int ...) then set next to NULL , since that node is the last node in the list.
when you add another node , you change the value of next to point to the node you just added and you set the value of next to NULL in the node you just created because now that is the last node in the list .
you can repeat this to add as many nodes as your memory allow you to.
this link may help you to better understand structures
typedef struct marks {
int m;
struct marks *next;
} marks_t;
This way we define a structure so that a Linked List can be formed. Now we have defined the last variable next as a "structure pointer" which gives us the address of the next element in the list (i.e. as structure only)!
The last element does not point to any node (here marks structure) and hence the pointer variable has NULL value.
Now to define the first element:
marks_t *list;
if (list == NULL) {
list = (marks_t *) malloc(sizeof(marks_t));
}
list->m = 15;
list->next = NULL;
Now if we want to add an element next to this (i.e. second element):
marks_t *next1;
next1 = (marks_t *) malloc(sizeof(marks_t));
next1->m = 27;
next1->next = NULL;
list->next = next1; // Storing address of next1 structure in list

Deleting first node of linked list

I have to print a list of a set in c using linked lists (hence pointers). However,when I delete the first element of the list and try to print the list, it just displays a lot of addresses under each other. Any suggestions of what the problem might be? Thanks!
Delete function:
int delete(set_element* src, int elem){
if (src==NULL) {
fputs("The list is empty.\n", stderr);
}
set_element* currElement;
set_element* prevElement=NULL;
for (currElement=src; currElement!=NULL; prevElement=currElement, currElement=currElement->next) {
if(currElement->value==elem) {
if(prevElement==NULL){
printf("Head is deleted\n");
if(currElement->next!=NULL){
*src = *currElement->next;
} else {
destroy(currElement);
}
} else {
prevElement->next = currElement->next;
}
// free(currElement);
break;
}
}
return 1;
}
void print(set_element* start)
{
set_element *pt = start;
while(pt != NULL)
{
printf("%d, ",pt->value);
pt = pt->next;
}
}
If the list pointer is the same as a pointer to the first element then the list pointer is no longer valid when you free the first element.
There are two solutions to this problem:
Let all your list methods take a pointer to the list so they can update it when neccesary. The problem with this approach is that if you have a copy of the pointer in another variable then that pointer gets invalidated too.
Don't let your list pointer point to the first element. Let it point to a pointer to the first element.
Sample Code:'
typedef struct node_struct {
node_struct *next;
void *data;
} Node;
typedef struct {
Node *first;
} List;
This normally happens when you delete a pointer (actually a piece of the memory) which doesn't belong to you. Double-check your function to make sure you're not freeing the same pointer you already freed, or freeing a pointer you didn't create with "malloc".
Warning: This answer contains inferred code.
A typical linked list in C looks a little something like this:
typedef struct _list List;
typedef struct _list_node ListNode;
struct _list {
ListNode *head;
}
struct _list_node {
void *payload;
ListNode *next;
}
In order to correctly delete the first element from the list, the following sequence needs to take place:
List *aList; // contains a list
if (aList->head)
ListNode *newHead = aList->head->next;
delete_payload(aList->head->payload); // Depending on what the payload actually is
free(aList->head);
aList->head = newHead;
The order of operations here is significant! Attempting to move the head without first freeing the old value will lead to a memory leak; and freeing the old head without first obtaining the correct value for the new head creates undefined behaviour.
Addendum: Occasionally, the _list portion of the above code will be omitted altogether, leaving lists and list nodes as the same thing; but from the symptoms you are describing, I'm guessing this is probably not the case here.
In such a situation, however, the steps remain, essentially, the same, but without the aList-> bit.
Edit:
Now that I see your code, I can give you a more complete answer.
One of the key problems in your code is that it's all over the place. There is, however, one line in here that's particularly bad:
*src = *currElement->next;
This does not work, and is what is causing your crash.
In your case, the solution is either to wrap the linked list in some manner of container, like the struct _list construct above; or to rework your existing code to accept a pointer to a pointer to a set element, so that you can pass pointers to set elements (which is what you want to do) back.
In terms of performance, the two solutions are likely as close so as makes no odds, but using a wrapping list structure helps communicate intent. It also helps prevent other pointers to the list from becoming garbled as a result of head deletions, so there's that.

store line in linked list (c)

I think that I am having some issues with how linked lists work, please bear in mind that I am not an expert with C and that I have not worked with linked lists before.
I'm trying to take a text file with a list of things and store it in a linked list. I have the following code right now:
typedef struct linked_list {
struct linked_list *next_ptr;
char name;
float price1;
float price2;
}linked_list;
struct linked_list *first_ptr;
char name_temp;
int writeList(void) {
// read input files
FILE *sh_list;
sh_list=fopen("case1/shoppingList.dat", "rw");
if (sh_list == NULL) {
perror("Cannot open file, you seem to have something mixed up...");
exit(8);
}
struct linked_list *current_ptr;
current_ptr = first_ptr;
while ((fscanf(sh_list, "%s", &name_temp)) !=EOF) {
next_ptr = malloc(sizeof(linked_list));
strcpy(name, name_temp);
//move to next node and complete the same task
}
};
I stopped at the //move... because I am struggling to get the code correct - my IDE is giving me errors. Similarly, I can't get it to read the variable "name" which I need to do in order to copy the string to the node.
You are getting next_ptr as undeclared because you have not delcared it.
Your code should look something like this ...
linked_list *next_ptr;
char name_temp[MAX_SIZE];
while ((fscanf(sh_list, "%s", &name_temp)) !=EOF) {
next_ptr = malloc(sizeof(linked_list));
strcpy(next_ptr->name, name_temp);
next_ptr->next_ptr = first_ptr;
first_ptr = next_ptr;
}
You should also make the declaration of name in linked list to be:
char name[MAX_SIZE];
In your fscanf statement you specify that you're inputting a string, %s, but name_temp has type char. You're going to have to change it to a char array, like so:
char name_temp[100];
You'll have to make the array sufficiently big for your purposes. You'll also have to change the type of name:
char name[100];
The error you are getting is because name and next_ptr are part of the struct linked_list but you you have to create an instantiation of linked_list to access name. In your case, you have the current_ptr so you should change it to:
current_ptr->next_ptr = malloc(sizeof(linked_list));
strcpy(current_ptr->name, name_tmp);
it should be strcpy(current_ptr->name, name_temp);
You are trying to refer to the name variable in the structure, which needs to be accessed through the current_ptr.
so similarly it should be current_ptr->next_ptr = malloc(sizeof(linked_list));
To access variable in a structure, you need to use the . operator. When you are using a pointer to a structure, then you access its variables using the ->operator.

Make a local copy of data going into a linked-list

this one is not homework, it just the fact that i've been out of school for more than 20 years and I NEVER had to use linked lists for anything at all. So i'm out of my element here.
anyway, I have
struct Data_Struct {
char *Name;
char *Task;
char *Pos;
struct Data_Struct *Next;
};
typedef struct Data_Struct MyData;
Which is a pretty simple struct for a linked list.
As you can see the data is stored in char* and when I populate the list I copy the pointers there.
All is well. The problem begins when those pointers get overwritten and I lose the original data. I tried copying the data into local char* malloc'ed on every "list_add()" but I keep on either crashing or losing data.
Yes, I do allocate enough and yes, I do copy the data and check that it's copied correctly. I tried with memcpy, strcpy, etc ,etc.
I'm out of things to try so my question is, how do I make sure that the data I will be adding to the list is local. how do I copy those char* going into the list into local variables.
while an explanation on how to do this and why my code is bad is OK, I prefer code with the explanation.
Thank you.
Jess.
You don't want to "copy into local", you want to do as you say you've done; allocate fresh memory using malloc(), store the data there, and save the pointers in the list node.
If you have it, you can use strdup() to combine the malloc()+strcpy() into one call.
It's hard to understand under what circumstances you experience that the data is overwritten, what are you doing to the list node(s) that cause this?
A basic prepend ought to look like this, for your node definition:
MyData * list_prepend(MyData* head, const char *name, const char *task,
const char *pos)
{
MyData *node = malloc(sizeof *node);
node->Name = strdup(name);
node->Task = strdup(task);
node->Pos = strdup(pos);
node->Next = head;
return node;
}
Obviously this lacks error handling (malloc() and strdup() can both fail). Note that it's a prepend, and that the new head of the list is returned.

Resources