Problem with structs and mem. allocation

N

Nafai

Hi. Have a look at this program:

struct tmp {
short a;
int b;
short c;
};

int main()
{
ofstream f("tmp.bin",ios::binary);
tmp t; t.a=5; t.b=2000; t.c=2;

f.write((const char*)&t.a,sizeof(short));
f.write((const char*)&t.b,sizeof(int));
f.write((const char*)&t.c,sizeof(char));
f.close();

ifstream g("tmp.bin", ios::binary);
tmp s;

g.read((char*)&s,sizeof(tmp));

cout << s.a << " " << s.b << " " << s.c << " " << endl;
cout << &s.a << " " << &s.b << " " << &s.c << " " << endl;
g.close();
}

The output should be (supposing &s==0x22fd48):

5 2000 2
0x22fd48 0x22fd4a 0x22fd4e

But the actual output is:

5 131072 -2764
0x22fd48 0x22fd4c 0x22fd50

So the problem is that the parts of the struct are not continuous in
memory!!! I mean, s.b should start 2 bytes after &s but actually it starts
after 4 bytes. That's why the g.read(...) doesn't work.

I tried:

but: cout << *((int*)((char*) (&s)+sizeof(short)))

and it output 2000.

In conclusion: &s+sizeof(s.a) != &s.b

Is all this my mistake or am I right??? I'm looking forward to an
explanation.

Thanks!
 
J

John Harrison

Nafai said:
Hi. Have a look at this program:

struct tmp {
short a;
int b;
short c;
};

int main()
{
ofstream f("tmp.bin",ios::binary);
tmp t; t.a=5; t.b=2000; t.c=2;

f.write((const char*)&t.a,sizeof(short));
f.write((const char*)&t.b,sizeof(int));
f.write((const char*)&t.c,sizeof(char));
f.close();

ifstream g("tmp.bin", ios::binary);
tmp s;

g.read((char*)&s,sizeof(tmp));

cout << s.a << " " << s.b << " " << s.c << " " << endl;
cout << &s.a << " " << &s.b << " " << &s.c << " " << endl;
g.close();
}

The output should be (supposing &s==0x22fd48):

5 2000 2
0x22fd48 0x22fd4a 0x22fd4e

But the actual output is:

5 131072 -2764
0x22fd48 0x22fd4c 0x22fd50

So the problem is that the parts of the struct are not continuous in
memory!!! I mean, s.b should start 2 bytes after &s but actually it starts
after 4 bytes. That's why the g.read(...) doesn't work.

Right. Now what made you think that they had to be contiguous in memory?
I tried:

but: cout << *((int*)((char*) (&s)+sizeof(short)))

and it output 2000.

In conclusion: &s+sizeof(s.a) != &s.b

Is all this my mistake or am I right??? I'm looking forward to an
explanation.

Compilers are allowed to add 'padding' between the data members of a struct
or class. They do this for efficiency reasons. Most compilers will have
compiler specific options to alter the padding or to turn it off entirely.

Alternatively you could read your data in the same way that you wrote it,
that would be the sensible thing to do.

john
 
N

Nafai

Right. Now what made you think that they had to be contiguous in memory?

I have seen most times things like g.read((char*)&s,sizeof(tmp)); to read a
full struct with one instruction.
 
J

John Harrison

Nafai said:
I have seen most times things like g.read((char*)&s,sizeof(tmp)); to read a
full struct with one instruction.

That code is OK providing that you read and write in the same way. Your
mistake was reading one way and writing another. Providing you don't mind
any padding bytes being written out to the file, you can read and write a
whole struct in one go.

And of course any binary read or write is not portable across machines,
compilers, etc. etc.

john
 
K

Karl Heinz Buchegger

Nafai said:
I have seen most times things like g.read((char*)&s,sizeof(tmp)); to read a
full struct with one instruction.

Right. Now search the same program and you will find that those programs
also write a struct in one rush

g.write( char*)&s, sizeof(tmp) );

and *never* by writing each struct member individually.
(Except of course when the programmer was aware of the padding issue
and took it into account. But thats rare).
 
N

Nafai

I came across this problem when reading a BMP file. I defined a struct for
the header and I got into trouble when reading the whole struct. So I have
to read each element of the header (more than 20) one by one? Isn't there
any easier way?
 
?

=?ISO-8859-15?Q?Juli=E1n?= Albo

Nafai said:
I came across this problem when reading a BMP file. I defined a struct for
the header and I got into trouble when reading the whole struct. So I have
to read each element of the header (more than 20) one by one? Isn't there
any easier way?

Write a file that describes the format of the header, and write a program
that reads this file and generates the code for reading the header.

Of course is more work, but is reusable.
 
J

John Harrison

Nafai said:
I came across this problem when reading a BMP file. I defined a struct for
the header and I got into trouble when reading the whole struct. So I have
to read each element of the header (more than 20) one by one? Isn't there
any easier way?

Well to be strictly portable you have to read the file one byte at a time,
not just one field at a time. How do you know that the format of an integer
in the BMP file (say) corresponds to the format of an int in your compiler?

If you don't mind not being portable then you should look at compiler
options or pragmas that will allow you to control the padding in your
struct. Then you can read the whole struct in one go.

john
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top