• C

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.
GrepsyAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

sunnycoderCommented:
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
Jaime OlivaresSoftware ArchitectCommented:
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 */
fridomCEO/ProgrammerCommented:
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
Your Guide to Achieving IT Business Success

The IT Service Excellence Tool Kit has best practices to keep your clients happy and business booming. Inside, you’ll find everything you need to increase client satisfaction and retention, become more competitive, and increase your overall success.

brettmjohnsonCommented:
I can think of three reasons you would get a segfault:

1) You did not allocate memory for the structure you are reading from disk.  
Allocate the memory for the structure first:

   struct myitem * myitem = (struct myitem *)malloc(sizeof(struct myitem));
   fread(myitem, sizeof(myitem), fp);
   ...


2) You wrote a memory pointer to the file.  If the structure contains a pointer
(the memory address of other data), simply writing out that structure's data
(including the enclosed memory address) will succeed.   However reading the
structure back into memory from disk at some later time will fail since the archive
memory address is no longer valid.  If your structure contains a pointer to some
other data, you must recursively write (and read) that data by value rather than
by reference:

void item_write(fd, struct item *item)
{
    gchar_write(fd, item->name);
    gchar_write(fd, item->image);
    int_write(fd, item->width);
    ...
}

...
void gchar_write(FILE * fd, gchar * gch)
{
    if (gch == NULL)
       fwrite(0, sizeof(int32), fd);
    else {
       int32 len = glen(gch) + 1;
       fwrite(sizeof(int32),1, fd);  // write string length first
       fwrite(gch, len, fd); // then write string contents
   }
}


3) You wrote the raw data, then read it back on a machine with a different native
word size (32 vs 64 bit) and/or different endianness (PowerPC vs x86).  In this case,
the data written does not correctly match up with the data read, causing the reader
to get mis-aligned or non-sensical values.

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Jaime OlivaresSoftware ArchitectCommented:
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.
fridomCEO/ProgrammerCommented:
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
fridomCEO/ProgrammerCommented:
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
Jaime OlivaresSoftware ArchitectCommented:
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.
fridomCEO/ProgrammerCommented:
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
cwwkieCommented:
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.
fridomCEO/ProgrammerCommented:
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

   
Jaime OlivaresSoftware ArchitectCommented:
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.
fridomCEO/ProgrammerCommented:
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
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
C

From novice to tech pro — start learning today.