Using unions with structures - c

I have a structure like this:
struct data
{
char abc[10];
int cnt;
struct data *next, *prior;
};
struct data *start, *last;
struct data *start1, *last1;
struct data *start2, *last2;
The integer 'cnt' can have two values. The pointers:
struct data *start, *last;
are used to link all data with all values of 'cnt'. The pointers:
struct data *start1, *last1;
struct data *start2, *last2;
are used to link data when the value of 'cnt' is either 1 or 2. My problem is that when I change the value of 'abc' or 'cnt' for one linked list, say 'start->abc', the value 'start1->abc' and 'start2->abc' are unchanged because they live in different memory locations.
I would like a change in data under one list to be reflected in the other two lists. I believe 'unions' could help me do this but I don't know how to set it up.
Any help appreciated!

Nope, can't be done.
If even you come up with a solution that uses unions to get this done, you'll essentially have some data objects allocated in such a way that they overlap each other in memory. You'd end up with a contiguous memory block.
Rather than that, disregard the linked list altogether and use an array:
struct data {
char abc[10];
int data;
}
struct data datas[50];
struct data* some = datas[20];
struct data* prev = some - 1;
struct data* next = some + 1;
(Don't go out of bounds.)
If you really want a linked list for some reason, the whole point of them is that each element can be anywhere in the memory. This means that each element needs to remember the address of the next and the previous in order to allow two-way navigation.
Therefore, rather than thinking about union tricks, just make a function insertData or removeData that do basic operations on a list and also fixes all the pointers in neighbouring elements.

char global_abc[10];
int global_cnt;
struct data
{
char *abc;
int *cnt;
struct data *next, *prior;
};
start->abc = start1->abc = start2->abc = global_abc;
start->cnt = start1->cnt = start2->cnt = aglobal_cnt;
And now when you changed the
strcpy(start->abc, "any");
then it will be changed for the other elements.
And when you changed the
*(start->cnt) = 5;
then it will be changed for the other elements.

If you want the data to live on two lists simultaneously, the "all" list and the "cnt" list, then you need two sets of start, last pointers in the structure.
struct data
{
char abc[10];
int cnt;
struct data *next_all, *prior_all;
struct data *next_cnt, *prior_cnt;
};
When you change the value of cnt, you must remove the data from the next_cnt, prior_cnt list (corresponding to start1, last1 or start2, last2) and add it to the other.

Use a set of arrays to hold the data, and use a pointer to those arrays from your structures. Then "linked" entries can point to the same data buffer...
struct data
{
char* abc;
int cnt;
struct data *next, *prior;
};
struct data *start, *last;
struct data *start1, *last1;
struct data *start2, *last2;
char abcBuffer[2][10];
and in some function somewhere...
start->abc = abcBuffer[start->cnt];
start1->abc = abcBuffer[start1->cnt];
start2->abc = abcBuffer[start2->cnt];
In this case, changing the content of abcBuffer[n] will reflect the same change across all of the structures linked to that buffer. The key, however, is that you cannot do this using a "shared" structure such as a union, but have to manage it in your code.

Related

struct inside struct : to point or not to point?

I'd like to understand the difference between using a pointer and a value when it comes to referencing a struct inside another struct.
By that I mean, I can have those two declarations:
struct foo {
int bar;
};
struct fred {
struct foo barney;
struct foo *wilma;
}
It appears I can get the same behavior from both barney and wilma entries, as long as I de-reference accordingly when I access them. The barney case intuitively feels “wrong” but I cannot say why.
Am I just relying on some C undefined behavior? If not, what would be the reason(s) to opt for one style over the other?
The following code shows how I come to the conclusion both use cases are equivalent; neither clang nor gcc complain about anything.
#include <stdio.h>
#include <stdlib.h>
struct a_number {
int i;
};
struct s_w_ptr {
struct a_number *n;
};
struct s_w_val {
struct a_number n;
};
void store_via_ptr(struct s_w_ptr *swp, struct s_w_val *swv) {
struct a_number *i = malloc(sizeof(i));
i->i = 1;
swp->n = i;
swv->n = *i;
}
void store_via_val(struct s_w_ptr *swp, struct s_w_val *swv) {
struct a_number j;
j.i = 2;
swp->n = &j;
swv->n = j;
}
int main(void) {
struct s_w_ptr *swp = malloc(sizeof(swp));
struct s_w_val *swv = malloc(sizeof(swv));
store_via_ptr(swp, swv);
printf("p: %d | v: %d\n", swp->n->i, swv->n.i);
store_via_val(swp, swv);
printf("p: %d | v: %d\n", swp->n->i, swv->n.i);
}
It's perfectly valid to have both struct members in a struct and have pointers to struct in a struct. They must be used differently but both are legal.
Why have a struct in a struct ?
One reason is to group things together. For instance:
struct car
{
struct motor motor; // a struct with several members describing the motor
struct wheel wheel; // a struct with several members describing the wheels
...
}
struct car myCar = {....initializer...};
myCar.wheel = SomeOtherWheelModel; // Replace wheels in a single assign
myCar.wheel.pressure = 2.1; // Change a single wheel member
Why have a struct pointer in a struct?
One very obvious reason is that is can be used as an array of N structs by using dynamic allocation of N times the struct size.
Another typical example is linked lists where you have a pointer to a struct of the same type as the struct containing the pointer.
There are several advantages of having a struct in a struct instead of having a pointer to struct in a struct:
It requires less memory allocation. In the case where you have a pointer to a struct in a struct, the compiler will allocate memory to store the pointer to the struct within the parent struct and separately allocate the memory for the child struct.
Additional instructions are typically required to access the contents of the child struct. For example consider that the program is reading the contents of the child struct. If a struct within a struct is used, the program will apply an offset to the address of the variable and read the contents of that memory location. In the case of a pointer to a struct in a struct, the program will actually apply an offset to the parent struct variable address, fetch the address of the child struct, then read from memory the contents of the child struct.
A separate variable needs to be declared for both the parent and child struct and if an initializer is used, then a separate initializer is needed. In the case of a struct in a struct only one variable must be declared and a single initializer is used.
In cases where dynamic memory allocation is used, the developer must remember to deallocate memory for both the child and parent objects before the variables fall out of scope. In the case of struct in a struct the memory must be freed for only one variable.
Lastly, as is shown in the example, if a pointer is used, Null checking may be necessary to ensure that the pointer to the child struct has been initialized.
The primary advantages of having a pointer to a struct in a struct would be if you needed to replace the child struct with another struct within the program, such as a linked list. A less common case might be if the child struct can be of more than one type. In this case you might use a void * type for the child. I may also use a pointer within a struct to point to an array in case where the array pointed to may vary in size between instances.
Based on my knowledge the case shown in the example above, I would be inclined to use a struct in a struct, since both objects are of fixed size and type and since it appears that they would not need to be separated.
C structures can be used to group related data, such as the title of a book, its author, its assigned book number, and so on. But much of what we use structures for is creating data structures (in a different sense of the word “structure”) in memory.
Consider that the book’s author has a name, a date of birth, other biographical information, a list of books they have written, and more. We could include in the struct book a struct author that would contain all this information. But, if the author has written a hundred books, we could have 100 copies of all that information, one copy in each struct book. Further, we cannot continue the “contain the data inside the structure directly” model with the struct author, because it cannot contain a struct book for each book the author publishes if those struct book members also have to contain the struct author for the author—every object would have to contain itself.
It is more efficient to create one struct author and have each struct book for that author to link to their struct author.
Another example is that we use pointers to create data structures for efficient access to data. If we are reading data for thousands of items and want to keep them sorted by name, one option is to allocate memory for some number of structures, read the data, and sort the data. When new data is read and we have used all the memory we allocated, we allocate new memory, copy all the old data to the new memory if necessary, and move some of the data so we can insert the new data in its proper place. However, we have many better options than that. We can use linked lists, binary trees, other kinds of trees, and hash tables.
These data structures effectively require using pointers. A binary tree will have a root node, and each node contains two pointers, one to a subtree of nodes that are earlier than it in the sorting order and another to a subtree of nodes that are later than it. We can look up items in the tree by following pointers to earlier or later nodes to find the right position. And we can insert items by changing a few pointers. If the tree happens to become unbalanced, we can rearrange nodes in the tree by changing pointers. The bulk of the data in the nodes does not have to be changed or copied, just some pointers.
We can also use pointers to have multiple structures for the same data. All the data about books could be stored in one place, and a tree ordered by name could contain nodes in which each node contained a pointer to the book structure and two pointers to subtrees. We could have one tree like this ordered by title of the book and another tree ordered by the name of the author and another tree ordered by the assigned book number. Then we can efficiently look up a book by title or author or number, but there is only one master copy of the complete book data, in the struct book objects. The look-up data is in the tree, which contains only pointers. That is much more efficient than copying all of the struct book data for each tree.
So the reasons we choose between use structures or pointers as members is not whether the C syntax allows us to refer to the data or not—we can get to the data in both cases. The reasons are because one method requires embedding data, which is inflexible and requires copying data, and the other method is flexible and efficient.
Let's consider at first this function
void store_via_ptr(struct s_w_ptr *swp, struct s_w_val *swv) {
struct a_number *i = malloc(sizeof(i));
i->i = 1;
swp->n = i;
swv->n = *i;
}
This declaration
struct a_number *i = malloc(sizeof(i));
is equivalent to the following declaration
struct a_number *i = malloc(sizeof( struct a_number * ));
So in general the function can invoke undefined behavior when sizeof( struct a_number ) is greater than sizeof( struct a_number * ).
It seems you mean
struct a_number *i = malloc(sizeof( *i ) );
^^^
If you will split the function in two functions for each its parameter like
void store_via_ptr1( struct s_w_ptr *swp ) {
struct a_number *i = malloc(sizeof( *i ) );
i->i = 1;
swp->n = i;
}
and
void store_via_ptr( struct s_w_val *swv ) {
struct a_number *i = malloc(sizeof( *i));
i->i = 1;
swv->n = *i;
}
then in the first function the object pointed to by the pointer swp will need to remember to free the allocated memory within the function. Otherwise there will be a memory leak.
The second function already produces a memory leak because the allocated memory was not freed.
Now let's consider the second function
void store_via_val(struct s_w_ptr *swp, struct s_w_val *swv) {
struct a_number j;
j.i = 2;
swp->n = &j;
swv->n = j;
}
Here the pointer swp->n will point to a local object j. So after exiting the function this pointer will be invalid because the pointed object will not be alive.
So the both functions are incorrect. Instead you could write the following functions
int store_via_ptr(struct s_w_ptr *swp ) {
swp->n = malloc( sizeof( *swp->n ) );
int success = swp->n != NULL;
if ( success ) swp->n->i = 1;
return success;
}
and
void store_via_val( struct s_w_val *swv ) {
swv->n.i = 2;
}
When to include a whole object of a structure type in another object of a structure type or to use a pointer to an object of a structure type within other object of a structure type depends on the design and context where such objects are used.
For example consider a structure struct Point
struct Point
{
int x;
int y;
};
In this case if you want to declare a structure struct Rectangle then it is natural to define it like
struct Rectangle
{
struct Point top_left;
struct Point bottom_right;
};
On the other hand, if you have a two-sided singly-linked list then it can look like
struct Node
{
int value;
struct Node *next;
};
struct List
{
struct Node *head;
struct Node *tail;
};
Two problems:
In store_via_ptr you allocate memory for i dynamically. When you use s_w_val you copy the structure, and then leave the pointer. Which means the pointer will be lost and can't be passed to free later.
In store_via_val you make swp->n point to the local variable j. A variable whose life-time will end when the function returns, leaving you with an invalid pointer.
The first problem might lead to a memory leak (something you never care about in your simple example problem).
The second problem is worse, since it will lead to undefined behavior when you dereference the pointer swp->n.
Unrelated to that, in the main function you don't need to allocate memory dynamically for the structures. You could just have defined them as plain structure objects and used the pointer-to operator & when calling the functions.

Creating a map without using a struct as linked list

I have to create a struct for a map in C that contains a char* key and void* value which are stored together internally. Rather than having a linked list as an external struct, I have to allocate memory for a linked list cell which contains the link pointer, the key string, and the value together (contiguous).
The key and value data have to be stored directly in the cell and the cell has to be constructed at runtime. I get the beginning of this which would involve a struct like:
struct example {
int count_of_list_elem;
void *list;
}
But I don't know how this is possible w/o using another struct to form the linked list?
Would I have to create some cell within the struct itself, allocate memory to it and pass it three values? I'm just not sure how it would work.
EDIT: I need to use a void* array.
This is a standard way of handling dynamically sized structures in C:
struct example {
struct example *next; /* linked list */
int count_of_list_elem;
char data[1];
}
struct example *bigone = malloc (sizeof (struct example) + 10000);
if (!bigone)
// error
bigone -> next = NULL;
bigone -> count_of_list_elem = 10000;
memcpy (bigone -> data, <data source>);
You can declare a struct before you define it, so that you can refer to it within the struct itself:
typedef struct tagMyStruct* my_struct_pointer;
typedef struct tagMyStruct
{
char* KeyPtr;
void* ValuePtr;
my_struct_pointer NextPtr;
} my_struct;
static my_struct_pointer _listHeadPtr = NULL;
my_struct_pointer AddNewCellToList(char* keyPtr, void* valuePtr)
{
my_struct_pointer newCellPtr = malloc(sizeof(my_struct));
newCellPtr->KeyPtr = keyPtr;
newCellPtr->ValuePtr = valuePtr;
newCellPtr->NextPtr = _listHeadPtr;
_listHeadPtr = newCellPtr;
return(newCellPtr);
}
You will need to malloc appropriate storage for the Key and Value before calling AddNewCellToList.
You can refer to a struct type within its own definition:
struct example {
int count_of_list_elem;
void *list;
struct example *next;
}
Your handle on the whole linked list is just a pointer to the first element:
struct example *head;
You will want to be careful when you create a new node to initialize its next pointer to NULL to indicate that there are (initially) no elements after it.

How would you link a linked list with different struct

I have 2 different stuct
typedef struct name {
char*markerName;
struct test *next;
}name_t;
typedef struct test {
int grade;
int studentNumber;
struct test *next;
}test_t;
and this function
void test(name_t* marker1,int data)
{
test_t *temp= malloc(sizeof(test_t));
test_t *location=NULL;
temp->grade=data;
temp->next=NULL;
location=marker1->next;
if(location==NULL)
{
// printf("%i \n",temp->grade);
marker1->next=temp;
}
else
{
while(location!=NULL)
{
printf("%i \n",location->grade);
printf("%p \n",location->next);
location=location->next;
}
location=temp;
}
}
the problem is we are creating an array of the stuct name and creating a linked list of the test after EACH ELEMENT OF THE ARRAY. How would I link the node of the struct name into the stuct test?
I printed the next out and they keep pointing to NULL pointer.
Strictly speaking, a linked list can only contain one data type. If you want to have a list containing both structure types, you can emulate this using a union:
struct name {
char* markerName;
};
struct test {
int grade;
int studentNumber;
};
// Indicates the type of data stored in the union
enum dtype { NONE, NAME, TEST };
// Combination of the above structures, suitable for a mixed-type list
struct combo {
struct combo* next; // Next structure in the linked list
enum dtype type; // Indicates which of the union fields is valid
union {
struct name name;
struct test test;
};
};
This stores both sets of data in a single structure, allows you to make lists from the structures, and enables you to keep track of which type of data is currently valid.
You are overshooting past the end of your linked list. You end up with 'NULL' for your location variable, which, even if it could be assigned, is still a local variable that goes out of context when your function exits. Your while loop should look more like this:
while(location->next != NULL)
{
printf("%i \n",location->grade);
printf("%p \n",location->next);
location = location->next;
}
location->next = temp;
What about a struct with two types of next pointers: one of type name_t and other of type test_t. You can use the one you want for linking and leave the other one NULL. I hope I understood your question correctly.
You could use a pointer to type void. This, of course, assumes that you know the type of the next object somehow.
When you want to create a heterogeneous data structure, it might be smarter to have only one struct type of nodes, and have two "payload" variables in the nodes, one which tells you the type of the node, and a pointer to a structure with the actual data.

structure with linked-list memory dump

is there any standard approach which I've missed at school to dump C structure with nested linked lists on disk in reasonable way?
What I don't want to do is:
use protocol-buffers or any other like serializators,
don't want to create JSON, XML or other
I've few ideas:
allocate accurate memory amount (or extend existing one) and manage it by myself, placing list elements in stack like approach using some additional fields to manage relative addresses. When necessary dump block on disk. Having procedures to map block from disk create desirable structure being aware of Byte-order.
push main structure to file, then push List elements, store information about list in the header of a file.
To image this I'll give some more details posting example code:
typedef struct{
int b;
List *next;
}List;
typedef struct{
float b;
List2 *next;
}List2;
typedef struct{
List *head;
List *tail;
} info;
typedef struct{
List2 *head;
List2 *tail;
} info2;
struct data
{
int a;
char t[10];
info first;
info second;
info2 third;
};
cheers
P.
EDIT:
I've extended main structure, seems like previous one haven't indicate the problem fully.
I'm aware that pointers on disk are useless.
Ideas and pseudocode allowed.
There's no neat way to do this, as these will have memory addresses, and the next time it is read in, it will contain memory addresses that could possibly be invalid...the only thing you could do is have a holding area for data to be read/written, let's look at how to write the data to disk based on the contents of the linked list...
struct rwBufferData{
int a;
char t[10];
};
and fill the 'rwBufferData' prior to writing by using memset and memmove
struct rwBufferData rwBuf;
struct data *dataPtr;
memset(&rwBuf, '\0', sizeof(struct rwBufferData));
memmove(&rwBuf, dataPtr, sizeof(struct rwBufferData));
Now you can then write rwBuf to file... I'll leave the reverse operation as an exercise...
I have not understood your problem correctly, but dumping a struct to disk and reading it back reliably has multiple issues.
Most important one is structure padding or byte stuffing. So you would have to take care of that also.
Serialize the data in the order it's held in the linked list, record-style to a file. fwrite is particularly good for this. Be sure to dereference pointers, and be aware of the role endianness plays in this.
Here's some vague pseudocode:
List *list_new();
List *list_add(List *, void *data);
List *list_next(List *);
while (node) {
fwrite(node->data, sizeof(node->data), 1, fp);
node = list_next(node);
}
Rough code for reading back into a live list:
List *node = list_new();
while (true) {
struct data *buf = malloc(sizeof(*buf));
if (1 != fread(buf, sizeof(*buf), 1, fp))
break;
list_add(node, buf);
}
Update0
If you begin to nest more advanced structures such as other linked lists, variable length strings etc., you'll need to provide types and lengths for each record, and a way to nest records within other records.
As an example, if your top level linked list had a data member that was another list, you'd be best to store that member as a nested record, complete with a length, and type field. Alternatively, you could define sentinel records, such as \0 for character strings (an obvious choice), and zeroed blocks for struct data.

Deep copy of graph structure

I have a graph structure in C and want to make a deep copy of it (including nodes and edges).
The structure looks like this:
struct li_list {
struct li_node n;
};
struct li_node {
struct li_node *next, *prev;
};
struct gr_graph {
struct li_list nodes;
int nodecount;
};
struct gr_node {
struct li_node node;
struct gr_graph *graph;
int pred_count, succ_count;
struct li_list pred, succ;
};
struct gr_edge {
struct li_node succ, pred;
struct gr_node *from, *to;
unsigned long marks;
};
These structs do not exist as themselves, but "inherited" in another struct, like this:
struct ex_node {
struct gr_node _; // "Superclass"
int id;
struct ex_node *union_find_parent;
...
}
Is there an elegant solution of creating a deep copy such a structure, including updating references to the copies?
Note: Members of nested structs do not point to the root struct it contains, but to their related nested struct (for instance, ex_node._.pred.n.next points to a ex_edge._.pred). This implies tedious pointer arithmetic when these must be updated.
My solution up to now is
Memcopy all structs
Iterate through all copies
Call a bunch of macros for all fields that contain references (Due to missing RTTI in C, I probably won't come around this)
The macros use
offsetof to calculate the address of the root struct
Retrieve the address of the copied equivalent
offsetof to make the pointer point to the correct nested struct
Is there any easier way to do this? I am also afraid that I forget to add a macro call when I add more fields.
I don't think you can do a deep-copy per se, as the pointers will have a memory address assigned to the pointers, the best way I can think of a deep-copy is to simply allocate a new graph structure and copy the data (not pointers) and build it up from there by mallocing the new pointers and adjust the pointers in the ex_node structure. That would be a more thorough solution...
Hope this helps,
Best regards,
Tom.
Sounds ok. My $0.02:
Not sure why you need both li_list and li_node. Further, don't you need a data member for li_node?
The overall structure looks a bit complex (of course, I don't know your requirements) and smells of C++ style design (pardon me, if I am wrong)
memcpy is not required. A simple assignment suffices.
Define a function pointer member for each structure with pointer members, so that you can do:
So:
struct foo {
int datum;
int *p;
foo_copy pfoo;
};
typedef void (*foo_copy)(const struct foo *src, struct foo *dst);
void foo_cp(const struct foo *src, struct foo *dst)
{
*dst = *src; // copy non-pointer data
dst->p = malloc(sizeof *dst->p);
dst->p = *src->p;
}
// somewhere else
struct foo s;
// initalize
struct foo *t = malloc(sizeof *t);
s.copy(&s, &t);
and nested types call appropriate member copy methods ...
memcpy all structs and create a sorted list where each entry contains address of original struct and address of copy of struct.
Now iterate through all copies. For each pointer variables in all copied structs, search pointer in the sorted list and replace it with the address of its copy.
Yes, there is an elegant solution using spanning trees and the decorator pattern.
-First, build a spanning tree of the graph. You can use a DFS (Depth First Search)
or a BFS(Breadth First Search) to achieve this. Use the decorator pattern to give
each each visited node a unique identifier.
-Next, (or at the same time) traverse the spanning tree from start to finish
and begin building your second tree by allocating new nodes and connecting
the edges that form the spanning tree.
-Finally, take one more pass through the spanning tree, and using the synchronized
identifiers, connect the remaining missing edges in the new graph, so that they match the
connectivity of the old graph.
(e.g. If node5 in graph1 has edges connecting to node7 and node 11, then
use the ordering of graph2 to connect its node5 to its node7 and 11.)

Resources