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.
Related
I am working on a shared library (aka dll) implementation that can be consumed by other C programs. As a data contract, I wish to be able to return a list of variable length from one of my methods. Say, the structure I wish to return is my_data_type, what should I put as a contract data structure?
I can think of something like this:
struct data_type_list
{
my_data_type* data_list;
int count;
};
where the consumer knows there are count elements in the list and consumer terminates the array at data_list[count-1].
Any other ideas?
It really depends on how the user of your library utilizes the returned data. I would probably go with your solution if the data is pretty much read-only (in a sense that the user doesn't want to write to it but just iterates over every element pointed to by data_type_list.data_list). If the user wants to do extensive modifications like adding to or deleting data, a linked list would be the preferred data structure to use, I think:
/* example of a node in a singly-linked list */
struct list_node {
void *data;
struct list_node *next;
}
A NULL pointer indicates the end of the list while data can be accessed using the data member of every node. You could of course add a struct list_node *prev member that points to the previous node, too (yielding a doubly-linked list).
So I'm creating a system to control inventory. I have a text file which contains all the items in the inventory.
as:
{component, stock code, count, price}
I am creating a struct to represent the stock (this is done in a header file):
typedef struct StockItem {
char *componentType;
char *stockCode;
int numOfItems
int price;
} StockItem;
I want to have a struct in which controls the entire inventory as a linked list, was wondering how would I do it so it would have a collection of StockItems. ( would this be a good way to do it)?
Also one more question, is it conventional to have the struct as a capital letter or not ?
EDIT:
typedef struct inventory {
struct StockItem item;
struct inventory *next;
}inventory;
inventory *pFirstNode = NULL;
inventory *pLastNode = NULL;
void createNewList(struct StockItem *item){
// Set aside enough space in memory for this struct
inventory *pNewStruct = (inventory*) malloc(sizeof(inventory));
// We can assign the value directly for the structs
// reference for the next struct in the linked list
pNewStruct->next = NULL;
printf("Enter Product Name: ");
// The & is needed only because scanf() is used
pNewStruct->item = item;
// When the first struct is created all of the following
// refer to the same struct
pFirstNode = pLastNode = pNewStruct;
}
If a linked list is suitable in your case really depends on how you want to access your inventory. If you plan only to go through it in sequence a linked list is good. If you store an index to a specific inventory item and later retrieve that item out of your list, thats going to be incredibly slow. In this case a datastructure with an array (std::vector for example) is better. But all of that also depends on the amount of data you have. Theres nothing wrong querying a linkedlist by index that only has 100 elements. For your specific question on how you can create a collection, are you limited to c, or can you use c++? The std namespace has a lot of useful collections including an implementation for a linkedlist and a vector which basically is an array, that dynamically resizes.
As you want to limit yourself to c, you will have to write your list implementation yourself. Its nothing that i would recommend somone who is still learning though. Implementing a decent list is really difficult. But if you really want to, this might be a good start. You also learn how linked lists work under the hood, which is a good thing considering a lot of people missuse linkedlists and wonder about bad performance afterwards.
Lets take the link above as an example. They defined a node struct:
typedef struct node {
int val;
struct node * next;
} node_t;
The field "val" is type int. In your case this will be the type of item, that you want to store in your list. Its going to be StockItem or a pointer to a StockItem, depending on how you want to manage your memory. If you want to add a StockItem to the list, you wrap it into a node and assign the pointer to that node to the "next" field of the previous node. Everything is also really well explained in the link i provided.
What you got so far is fine. Next, to add a node to your list, you should set the lastNode pointer and the next pointer in lastNode:
pLastNode->next = pNewStruct;
pLastNode = pNewStruct;
I want to create a struct with 2 variables, such as
struct myStruct {
char charVar;
int intVar;
};
and I will name the structs as:
struct myStruct name1;
struct myStruct name2;
etc.
The problem is, I don't know how many variables will be entered, so there must be infinite nameX structures.
So, how can I name these structures with variables?
Thanks.
You should use an array and a pointer.
struct myStruct *p = NULL;
p = malloc(N * sizeof *p); // where N is the number of entries.
int index = 1; /* or any other number - from 0 to N-1*/
p[index].member = x;
Then you can add elements to it by using realloc if you need to add additional entries.
Redefine myStruct as
struct myStruct {
char charVar;
int intVar;
struct myStruct *next;
};
Keep track of the last structure you have as well as the start of the list. When addding new elements, append them to the end of your linked list.
/* To initialize the list */
struct myStruct *start, *end;
start = malloc(sizeof(struct myStruct));
start->next = NULL;
end = start;
/* To add a new structure at the end */
end->next = malloc(sizeof(struct myStruct));
end = end->next;
end->next = NULL;
This example does not do any error checking. Here is how you would step along the list to print all the values in it:
struct myStruct *ptr;
for(ptr = start; ptr != NULL; ptr = ptr->next)
printf("%d %s\n", ptr->intVar, ptr->charVar);
You not have to have a distinct name for each structure in a linked list (or any other kind of list, in general). You can assign any of the unnamed structures to the pointer ptr as you use them.
So, how can I name these structures with variables?
I think every beginner starts out wanting to name everything. It's not surprising -- you learn about using variables to store data, so it seems natural that you'd always use variables. The answer, however, is that you don't always use variables for storing data. Very often, you store data in structures or objects that are created dynamically. It may help to read about dynamic allocation. The idea is that when you have a new piece of data to store, you ask for a piece of memory (using a library call like malloc or calloc). You refer to that piece of memory by its address, i.e. a pointer.
There are a number of ways to keep track of all the pieces of memory that you've obtained, and each one constitutes a data structure. For example, you could keep a number of pieces of data in a contiguous block of memory -- that's an array. See Devolus's answer for an example. Or you could have lots of little pieces of memory, with each one containing the address (again, a pointer) of the next one; that's a linked list. Mad Physicist's answer is a fine example of a linked list.
Each data structure has its own advantages and disadvantages -- for example, arrays allow fast access but are slow for inserting and deleting, while linked lists are relatively slow for access but are fast for inserting and deleting. Choosing the right data structure for the job at hand is an important part of programming.
It usually takes a little while to get comfortable with pointers, but it's well worth the effort as they open up a lot of possibilities for storing and manipulating data in your program. Enjoy the ride.
I am sending student records over a socket. The database sends the client the number of students in the list and then sends the records one at a time. The problem I am coming across is that when there are no students the list starts with an uninitialized record. I thought about adding a new field to the records that stated if they were a new record or had been filled but that would mean changing A LOT of the code.
Is there an easy way to check a record to see if it has been filled. Below is the structure for the student records.
typedef struct student{
char lname[10], initial, fname[10];
unsigned long SID;
float GPA;
} SREC;
typedef struct node{
SREC student;
int deleted;
struct node *left;
struct node *right;
} NODE;
This is what initialization is done for the starting empty node.
NODE *lname = calloc( 1, sizeof(NODE) );
lname->left = NULL;
lname->right = NULL;
lname->deleted = 0;
If the null pointer on your system has an all-zero bit pattern (and it probably does), you don't need any of that code after calloc.
You can track if a record has been initialized by looking at any field that doesn't have a legitimate all-zero value. Maybe the lname or SID fields?
An uninitialized record could have anything in it. In fact, at a very low probability it could coincidentally have something interesting, or even the written exposition at the beginning of Star Wars in it. You can't rely on the contents of uninitialized memory.
As such you'll need to introduce something to track whether or not the data in the structure is filled in, and deliberately set it to the appropriate value (which is, in a way, initializing the structure.)
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.