Link to home
Start Free TrialLog in
Avatar of Grepsy
Grepsy

asked on

Writing a struct with pointers and GTK variables

I have a structure that looks as follows

typedef struct item
{
gchar *name;
gchar *image;

int width;
...
};

I create and populate the instance of my structure and call it myitem.

I save the file using fwrite(&myitem, sizeof(struct item), 1, fp) where fp is my file pointer.  When I open the file and read in the struct using fread(&myitem, sizeof(struct item), 1, fp) and try to access myitem.name i get a segmentation fault.  I have tried several things but cannot determine the problem.
Avatar of sunnycoder
sunnycoder
Flag of India image

Can you show us the code pls? ... Seg fault can be caused by almost anything from a dangling pointer to an & you missed before myitem
Well, simply you cannot save a pointer in a file and try to recover and access again.
So, fwrite(&myitem, sizeof(struct item), 1, fp) will never work
you have to store each struct member by member, and saving the string instead of char pointer. Something like:

char CR = '\r';
fwrite(myitem.name, strlen(myitem.name), 1, fp);   /* save first member */
fwrite(&CR,1,1,fp) /* use as string separator */
fwrite(myitem.image, strlen(myitem.image), 1, fp);   /* save 2nd member */
fwrite(&CR,1,1,fp) /* use as string separator */
fwrite(&myitem.width, sizeof(myitem.width), 1, fp);   /* save 3rd member, you can use sizeof for fundamental types, not for strings
/* etcetera */
Or course you can. You just have to be sure that you do not change the alignment while writing.

#include <stdio.h>
#include <stdlib.h>

struct foo {
  int a;
  int b;
  char str[50];
};

int main (void){
  struct foo foo_test = {1,2,"some string"};
  struct foo foo_val = {0};
  char *file_name = "t1.txt";
  FILE *fp = fopen(file_name, "w");

  if (fp == NULL){
    fprintf(stderr, "Could not open file %s\n", file_name);
    exit(EXIT_FAILURE);
  }

  fwrite(&foo_test, sizeof(foo_test), 1, fp);
  fclose(fp);
  fp = fopen(file_name, "r");
  fread(&foo_val, sizeof(foo_val), 1,fp);
  printf("a = %d, b = %d, str = %s\n", foo_val.a, foo_val.b, foo_val.str);
  fclose(fp);
 
 

  return 0;
}

Output:  /a.out
a = 1, b = 2, str = some string

It's not stable, and it's often a good idea to use another way of serialize structures but it's valid and will work as show here.

You may want to check:
http://computer.howstuffworks.com/c39.htm

Friedrich
ASKER CERTIFIED SOLUTION
Avatar of brettmjohnson
brettmjohnson
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
fridom evaluation is not correct, as stated by me and also by brettmjohnson, it is possible to save a pointer but it is absolutely unusable when you read again, usually causing a segmentation fault.
fridom example is a bad trick, it saves a pointer that points to a constant string, and loads it inmediately in the same proccess, so it points to the same memory position.
But try to save and load in different application instances, it won't work.
I suggest you try the code I posted. first you write the write function and after that the the read stuff. It will work. It's not a trick, it's perfect ok. The point is if you "know" the size of the structure fread and fwrite will work but if you introduct pointer to other elements you'll be out of luck. But saying it does not work is in that strict way not true.

Here's only the read code:
#include <stdio.h>
#include <stdlib.h>

struct foo {
  int a;
  int b;
  char str[50];
};

int main (void){
  struct foo foo_test = {1,2,"some string"};
  struct foo foo_val = {0};
  char *file_name = "t1.txt";
  FILE *fp;
  // FILE *fp = fopen(file_name, "w");
  // fwrite(&foo_test, sizeof(foo_test), 1, fp);

  fp = fopen(file_name, "r");
  fread(&foo_val, sizeof(foo_val), 1,fp);
  printf("a = %d, b = %d, str = %s\n", foo_val.a, foo_val.b, foo_val.str);
 
  fclose(fp);
 
 

  return 0;
}

and this is the output:

./a.out
a = 1, b = 2, str = some string

Of course it works because
a) I used the same compiler on the same platform and with the same alignment
b) the size of the structure is exactly known.

I did not say it's a good idea I just refused the categorical it does not work. It even have to work without the changes. If you feel I'm wrong about that please tell me exacly the location in the Standard which states that..

Friedrich
Now to something more useful for the OP. You should check the Berkeley DB Database which eases the saving of C structures. Or you should define a text format for you data, maybe it would be a good idea to save the data using XML.

Regards
Friedrich
I don't need to try, I know it works, but it mis-orient the asker, because it just work if you write and read inmediatly in the same process, it won't work in any other scenario. As a general rule, you never never must store a pointer into a file, I don't want to sound rude but you sample is just a dirty trick, doesn't have to do with proper alignment or compiler version.
Just try to divide your application in 2: one to write and the other to read. You will see it doesn't work, even with the same alignment and compiler.
I will just wait to other experts to opine.
Don't you read what I write/read in different processes and it will work!

You do not get how fwrite and fread works I'm afraid. They do not care what is in the file if I used another structure of the same size it would happily read that stuff, but of course the whole stuff is nonsense.

The example works because the length is exactly known, the alignment does not change  and the compilte does not change either.

And as before I did not write one should use it I just proved your wrong.

I posted just the reader. Do you see any writing in there?

Friedrich
Avatar of cwwkie
cwwkie

Friedrich,

please notice that you are not saving a pointer. The original question included a struct with a pointer:

typedef struct item
{
gchar *name;
gchar *image;

int width;
...
};

Your struct includes an array;

struct foo {
  int a;
  int b;
  char str[50];
};

in this case all 50 bytes are stored inside the struct. when you would have used char *str it would not work.
Well you're correct about this but an array is a pointer (often, not always...)

As long as you know how long any array is in that structure you can save it. You even can save things like

struct foo_1 {
    int a;


struct foo {
    struct foo_1 val_2...

and the like but you are fully write you can not store "just" a pointer. And this was asked here, but I posted another message which suggests a tool to use.

structure persitence is really a problem in C. Of course you can always store that stuff, but it's tedious work and unfortunatly it's error prone too.

I still have another suggestion: One could check a relational database for storing the structure of course you have to rebuild the structures yourself too, but this is IMHO easier to work with a few SQL statement as with some "ad-hoc self-invented" structure.

Other alternatives are of course
1) plane text files
2) some XML encoding (but of course this is a sub-category to 1)

Or one may think about a language which does have support for "serialization"

Regards
Friedrich

   
Regarding the pointer/array issue,
I reviewed your code again a you suggested put an array into a struct, but this is not the original question.
There are many reason to not to put a fixed size array into a struct, so my answer considers just storing a pointer, not an array.
About your proposed alternatives, plain files will be easier to manage in C, while XML could be more suitable for C++ applications.
I can not see any reason why C and plain text files should be easier then C++ with XML.
XML is always more work if you do not use the proper libraries. Plain text is easy with everything, just some sort of handling of the stuff you write to the file is needed of course.

Friedrich