Save struct with pointer members in file - c

I'm trying to save a struct into a .dat file and read it back in later.
struct myStruct{
char **one;
mytype **two;
mytype2 *three;
}
With an assigning function:
struct MyStruct get_struct() = {
char **pi = ...;
mytype **pa = ...;
mytype2 **po = ...;
MyStruct n = {pi, pa, po};
return n;
}
I originally tried to save this struct into a .dat file by doing this:
struct MyStruct s = get_struct();
myoutfile = fopen("file.dat", "w");
if (myoutfile == NULL) {
fprintf(stderr, "\nError opend file\n");
exit(1);
}
fwrite(&s, sizeof(struct MyStruct), 1, myoutfile);
fclose(myoutfile);
and read it back in with:
fread(&t, sizeof(struct MyStruct), 1, myinfile)
Now I learned, that this does not work (segmentation error), because I only save the location where the pointer points to, not the actual thing.
Now my question is, how can I do it properly? I have found some solutions for C++ but I need to stay in C.
EDIT:
Later on, I want to call a function which looks like this:
void work_with_struct(MyStruct s){
char ** xone = s.one;
mytype **xtwo = s.two;
mytype2 *xthree = s.three;
}
This post is related to this post, but as I could specify my mistake now, asking in a new post makes more sense to me.

As always in programming, you break up the task to smaller chunks, and break up smaller chunks to yet smaller chunks, until every chunk is easy.
int saveMyStruct (struct myStruct* myStruct, FILE* file) {
// what do I do here?!?!
// well it has three members
// so treat each one in sequence
int result;
result = saveStringArray(myStruct->one, file);
if (result >= 0)
result = saveMyTypeArray (myStruct->two, file);
if (result >= 0)
result = saveMyType (myStruct->three, file);
return result;
}
Note how the status is checked all the time. If you work with files, you need to check the status all the time.
What next? You need to write three functions mentioned above.
saveStringArray(char** stringArray, FILE* file)
{
// first save the length of the array, then save each individual string
int length = getStringArrayLength(stringArray);
int result = fwrite(&length, sizeof(length), 1, file);
if (result != 1)
return -1;
for (i = 0; i < length; ++i)
{
result = saveString(stringArray[i], file);
if (result < 0)
return -1;
}
return i;
}
And so on and so forth. I presume your array of pointers is NULL-terminated; if not, you need to have some other way to know its length.
Note how array length is always saved before array elements. This is because you will need to read your array later, and you will need to know where to stop. It will also be easy to allocate your array when you read it.

Related

how to write in a file a struct that has an array with Malloc

I try to print in a file the content of a structure that has a dynamic array inside and I think I'm not getting it
the struct looks like
struct save {
char s_ext[5];
char *s_bits;
long s_frec[256];
long int s_sim;
};
here I save the imformation in the struct
struct save *res = malloc(sizeof(struct save));
I try to malloc the array s_bits inside a struct
res->s_bits = malloc(sizeof(char) * sim);
if (res->s_bits == NULL) {
printf("error\n");
}
strcpy(res->s_bits, textDeco);
strcpy(res->s_ext, extension);
res->s_sim = sim;
for (i = 0; i < 15; ++i) {
printf("%ld -> %d:%d, ", i, res->s_bits[i], textDeco[i]);
}
printf("\n");
for (i = 0; i < 256; ++i) {
res->s_frec[i] = frecCopy[i];
}
open the file
FILE *save_struct = fopen("codi.dat", "w");
When I try to write the struct on a binary file using fwrite
if (fwrite(res, sizeof(struct save), 1, save_struct) != 0) {
printf("file created!\n");
} else {
printf("error\n");
}
it doesn't write a the elements of s_bits, which I don't want.
how do i get the elements with fread?
You allocate your struct like this:
struct save *res = malloc(sizeof(struct *res));
if(!res) // handle errror
res->s_bits = malloc(sim);
if(!res->s_bits) // handle error
When you use fwrite() to store the struct to a file, it will save pointer value res->s_bits but not the array it points to. The way to handle that is write out each field individually. When this gets annoying find a library to help you serialize your data (Cap'n Proto, protobuf, JSON etc). You should also consider SQLite.
As you only have one field like this you could make s_bits a flexible array member:
struct save {
char s_ext[5];
long s_frec[256];
long int s_sim;
char s_bits[];
};
and now you allocate it like this:
struct save *res = malloc(sizeof(struct *res) + sim);
and you would write it in a similar fashion:
fwrite(res, sizeof(*res) + res->s_sim, 1, save_struct)

How can I save these struct to a binary file?

I have two structs I am aiming to save to a binary file.
typedef struct {
int height;
int width;
int resistance_count;
Resistance** resistances; //contains a list of resistance*.
} Breadboard;
typedef struct {
int start_cell_col;
int end_cell_col;
int cell_row;
float resistance_value;
} Resistance;
I am somewhat unsure how I should be going about saving them. Since I need to keep track of the "resistance_count" variable to know how many resistances I will be saving, I have to save the breadboard first. To do that my attempt has been as follows:
bool save_breadboard(char* filename, Breadboard* bb_pointer) {
errno_t error_code;
FILE* fp_board;
/* Opens board.bin to save the board struct on. */
error_code = fopen_s(&fp_board, filename, "wb");
if (error_code != 0) {
return false;
}
size_t elements_written = fwrite(bb_pointer, sizeof(Breadboard), 1, fp_board);
if (elements_written == 0) {
return false;
}
fclose(fp_board);
return true;
}
With my current attempt I see a problem that I am also saving all the "Resistance**" which is perhaps unnecessary. I don't know if there is a way I could skip saving the resistance pointer pointers. But I don't think it will cause problem when I eventually read from it.
To save resistances I'm running into problems. Here is what I do:
bool save_resistances(char* filename, Breadboard* bb_pointer) {
errno_t error_code;
FILE* fp_resistances;
/* Opens resistances.bin to save the array of resistance pointers on. */
error_code = fopen_s(&fp_resistances, filename, "wb");
if (error_code != 0) {
return false;
}
size_t elements_written = fwrite(bb_pointer->resistances, sizeof(Resistance),
bb_pointer->resistance_count, fp_resistances);
if (elements_written == 0) {
return false;
}
fclose(fp_resistances);
return true;
}
I am pretty sure that I will be saving the resistance pointer this way. I cannot check with binary files, but if I am would dereferencing the resistance pointer help?
size_t elements_written = fwrite(*bb_pointer->resistances, sizeof(Resistance),
^ bb_pointer->resistance_count, fp_resistances);
Any help in helping me understand reading/writing to binary files would be much appreciated.
You are getting close to the solution. Just a couple of things missing...
With my current attempt I see a problem that I am also saving all the "Resistance**" which is perhaps unnecessary. I don't know if there is a way I could skip saving the resistance pointer pointers [...]
Yeah, it is unnecessary. Indeed when reading back you will have to discard it and overwrite it with a valid value. I wouldn't bother finding strange ways of skipping the pointer. I'd suggest writing out a NULL pointer instead to avoid mistakes when re-reading the data by doing something like:
void *tmp = bb_pointer->resistances;
bb_pointer->resistances = NULL;
size_t elements_written = fwrite(bb_pointer, sizeof(Breadboard), 1, fp_board);
bb_pointer->resistances = tmp;
Now, coming to the part where you actually save all your structures, this is wrong:
size_t elements_written = fwrite(bb_pointer->resistances, sizeof(Resistance),
bb_pointer->resistance_count, fp_resistances);
And doing *bb_pointer->resistances is also wrong. You want to save each Resistance struct, but your ->resistances is an array of pointers, so (1) saving the pointers (bb_pointer->resistances) is obviously wrong and (2) trying to save the structs as if they were contiguous in memory (*bb_pointer->resistances) is also wrong. The only thing you can do is loop over the array and dereference every single pointer, saving it separately:
for (int i = 0; i < bb_pointer->resistance_count; i++) {
if (fwrite(bb_pointer->resistances[i], sizeof(Resistance), 1, fp_resistances) != 1) {
// handle error
return false;
}
}
Finally, remember that fwrite returns the number of elements written which should always be equal to the requested number, so in general you need to check for errors with res != count, not with res == 0:
size_t elements_written = fwrite(bb_pointer, sizeof(Breadboard), 1, fp_board);
if (elements_written != 1) {
return false;
}

c Struct Array, Storing string and its occurrence and writing it to a file

so I'm having a little problem with my struct array not doing what its supposed to. I get no compiler warnings or errors when building the program.
int Array_Size=0;;
int Array_Index=0;
FILE *Writer;
struct WordElement
{
int Count;
char Word[50];
};
struct WordElement *StructPointer; //just a pointer to a structure
int Create_Array(int Size){
StructPointer = (struct WordElement *) malloc(Size * sizeof(StructPointer));
Array_Size = Size;
return 0;
}
int Add_To_Array(char Word[50]){
int Word_Found=0;
for(int i=0; i <= Array_Size && Word_Found!=1; i++)
{
if(strcmp(StructPointer[i].Word, Word)) // This should only run if the word exists in struct array
{
StructPointer[i].Count++;
Word_Found=1;
}
}
if(Word_Found==0) // if the above if statement doesnt evualate, this should run
{
strcpy(StructPointer[Array_Index].Word, Word); //copying the word passed by the main function to the struct array at a specific index
printf("WORD: %s\n", StructPointer[Array_Index].Word); // printing it just to make sure it got copied correctly
Array_Index++;
}
return 0;
}
int Print_All(char File_Name[50])
{
Writer = fopen(File_Name, "w");
printf("Printing starts now: \n");
for(int i=0; i < Array_Size; i++)
{
fprintf(Writer, "%s\t\t%d\n",StructPointer[i].Word, StructPointer[i].Count);
}
free(StructPointer);
return 0;
}
These functions get called from a different file, The Add_To_Array is called when the program reads a new word form the text file. That function is supposed to check if the word already exists in the struct array and if it does, it should just increment the counter. If it doesn't, then it adds it.
The Print_All function is called after all the words have been stored in the struct array. Its supposed to loop through them and print each word and their occurrence. In the text file, there are 2 of every words but my program outputs:
this 13762753
document -1772785369
contains 1129268256
two 6619253
of 5701679
every 5570645
word 3342389
doccontains 5374021
I don't know what to make of this as im really new to C programming... It's probably worth mentioning the if(Word_Foun==0) doesn't execute
StructPointer = malloc(Size * sizeof(*StructPointer));
This will be the correct allocation. Otherwise you will have erroneous behavior in your code. Also check the return value of malloc.
StructPointer = malloc(Size * sizeof(*StructPointer));
if(NULL == StructPointer){
perror("malloc failure");
exit(EXIT_FAILURE);
}
You are allocating for struct WordElement not a for a pointer to it. You already have a pointer to struct WordElement all that you needed was memory for a struct WordElement.
Also in the loop you are accessing array index out of bound
for(int i=0; i <= Array_Size && Word_Found!=1; i++)
^^^
It will be i < Array_Size.
In case match occurs you want to set the variable Word_found to 1.
if(strcmp(StructPointer[i].Word, Word) == 0){
/* it macthed */
}
Also Writer = fopen(File_Name, "w"); you should check the return value of fopen.
if(Writer == NULL){
fprintf(stderr,"Error in file opening");
exit(EXIT_FAILURE);
}
Also when you are increasing the Array_index place a check whether it might access the array index out of bound.
The more global variable you use for achieving a small task would make it more difficult to track down a bug. It is always problematic because the places from which data might change is scattered - making it difficult to manage.

C write struct to file

I am trying to save struct data to file. I saved the data this way.
node_trx * trx_list;
trx_list = calloc(1, sizeof(node_trx *));
trx_list->amount = "123123123";
trx_list->currency = 123;
trx_list->next_node = NULL;
if (1 != fwrite(trx_list, length, 1, f)) {
//error
}
free(trx_list);
Here is my struct:
typedef struct {
char amount;
int currency;
struct node_trx * next_node; } node_trx;
Main problem is after i saved struct to file and then after read, when print values, it is printing wrong values. For example: i stored currency as 123, then printed 6788576 this kind of numbers.
here is my reading code:
int read_last_trx_from_file (const char * file_name, node_trx * * trx, unsigned * trx_len)
{
FILE * f;
*trx = NULL;
if (NULL == (f = fopen(tools_get_full_filename_const(file_name), "rb")))
{
return 2; //error
}
size_t fsize;
fseek(f, 0, SEEK_END);
fsize = ftell(f);
fprintf(stdout, "file size: %zd\n", fsize);
if (!fsize)
{
fclose(f);
return 3; //no data
} else {
if (fsize == 1) {
return 3; // no data
}
}
rewind(f);
if (NULL != (*trx = (node_trx *) calloc(1, fsize)))
{
if (1 != fread(*trx, fsize, 1, f))
{
fclose(f);
free(*trx);
return 2; //error
}
}
fclose(f);
*trx_len = fsize;
return 0; //OK }
Main function that calls read function:
int display_trx() {
node_trx * card_data;
if (3 != read_last_trx_from_file(LAST_TRX_OBJECT, &card_data, &data_set_len)) {
if (card_data != NULL) {
printf("%s AMOUNT \n", card_data->amount);
printf("%d CURRENCY \n", &card_data->currency);
}
}
}
After i read this way , when i print amount data, segmentation fault error occurs. so why segment error occured?
And when i print currency, it printing 734364636 this kinda numbers. So why it prints wrong numbers.
Or i only wrote pointer of struct to file?
Please help me guys.
There are two obvious errors in your code.
In struct declaration, the type of amount is char, but when you initialize it in trx_list->amount = "123123123";, you assigned a string, or char[10] array (there is an extra one for NULL terminator).
In function display_trx, second printf, the result of &card_data->currency is int *, not int. If you want to print out currency, why don't follow the first print, use card_data->currency (without &)? You get that large number because you are printing pointer value implicitly converted into int, or the address of currency in card_data.
And there is one error which compiler will not warn you (because it is not syntactically wrong.). As BLUEPIXY said in the comments, when allocating and initializing trx_list, you should really use calloc(1, sizeof(node_trx)). You are allocating space for what pointer trx_list points to, not the pointer itself, so there should not be an asterisk in sizeof.
My suggestion is using a "smart" compiler, such as gcc, and enable warnings. This is a good practice (at least for me). Thank you!

C program crashes when trying to refer to struct array

I'm trying to make a simple database program, but when I get to this line
int idSearch(product* itens, int id) {
int i = 0;
for(i; i < size; i++) {
if(itens[i].id == id) //<=== This line
return i;
}
return ITEM_NOT_FOUND;
}
the program stops responding.
size is set as a global variable in the begining of the program
FILE* flog;
FILE* db;
FILE* sizef;
int size = 100;
this function is called by
void newProduct(product* itens, char name[64], int id, float price) {
int pos = idSearch(itens, 0);
if(idSearch(itens, id) != ITEM_NOT_FOUND) {
printf("Erro, o produto ja existe");
return;
}...
items is defined as
itens = (product*) calloc(sizeof(product), size);
and product is a struct defined as such
typedef struct{
char name[64];
int id;
float price;
int amount;
} product;
Firstly I thought the problem was that I was not using the -> operator, but when I tried the compiler says its not right.
I'm using Code::Blocks with GCC compiler on a Windows 7 x64
**EDIT: the whole code can be found here: http://hastebin.com/atulajubar.tex
Hope to hear answers soon, Thanks in advance
**EDIT: You're calling calloc() wrong. The signature is: void *calloc(size_t nelem, size_t elsize);
You're giving it the size first, then the number of elements. Switch that around and see if your problem is resolved.
Also, when calling (AFTER THE FIX:) itens = (product*) calloc( size, sizeof(product) );,
it's important to check to see that itens is not NULL after doing this. If calloc isn't able to give you back the right amount of memory, it returns a NULL pointer I believe. Check this, because if you're getting NULL back, that's your issue.
One good, easy, portable way of checking that would be:
if(!itens){
fprintf(stderr, "Error! Could not allocate memory!\n");
exit(1);
}
Also, as WhozCraig suggested, please make sure your code contains #include <stdlib.h>, a requirement of calloc() as per its man page.
This is wrong:
if((db = fopen(DB_PATH, RB))==NULL)
{
if((db = fopen(DB_PATH, RWB))==NULL)
{
exit(1);
}
else
{
itens = (product*) calloc(sizeof(product), size);
fwrite(itens, sizeof(product), size, db);
rewind(db);
}
}
fread(itens, sizeof(product), size, db);
If you have a DB_PATH file in the current working directory the first fopen() will succeed and the items allocation will never take place. Only if the file is not found, but is then successfully created will items contain a valid allocation, assuming calloc worked.
That else condition should be removed:
// note calloc parameter order addressed.
itens = calloc(size, sizeof(product));
if (itens == NULL)
{
perror("Failed to allocate items.");
exit(EXIT_FAILURE);
}
if((db = fopen(DB_PATH, RB))==NULL)
{
if((db = fopen(DB_PATH, RWB))==NULL)
exit(EXIT_FAILURE);
fwrite(itens, sizeof(product), size, db);
rewind(db);
}
fread(itens, sizeof(product), size, db);
There is a significant amount of error checking left to handle, but this needs to be addressed regardless.

Resources