Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Printing the last item of a structure twice

0 views
Skip to first unread message

Rafael Anschau

unread,
Jul 30, 2009, 6:11:39 PM7/30/09
to
Hi,

The following program reads a structure into a file 3 times, but when
I tell
it to print them all, it prints the last one twice. I apreciate any
help on what
am I doing to cause this.

Thank you,

Rafael

#include <string.h>
#include <string>
#include<iostream>

using namespace std;


int _tmain(int argc, _TCHAR* argv[])
{
char nf[]="1.tst";
string str="Hello world";
FILE *fp;


if ((fp=fopen(nf,"w+"))==NULL) {
std::cout<<"Erro nao abriu";
int a;
cin>>a;
exit(1);
}
typedef struct endereco {
string rua;
int n;


} ender;

ender meu,teu;

int i;

rewind(fp);
for(i=0;i<3;i++) {
cout<<"Nome da rua:\n";
cin>>meu.rua;
cout<<"Numero do ap:\n";
cin>>meu.n;

fwrite(&meu,sizeof(ender),1,fp);

}

cout<<"Listando\n";

rewind(fp);
while(!feof(fp)) {
fread(&teu,sizeof(ender),1,fp);
cout<<teu.rua<<"\n";
cout<<teu.n<<"\n";
}

int a;
cin>>a;

}

Jonathan Lee

unread,
Jul 30, 2009, 7:54:33 PM7/30/09
to
On Jul 30, 6:11 pm, Rafael Anschau <rafael.ansc...@gmail.com> wrote:
> The following program reads a structure into a file 3 times

Yes, but that's not actually what you want it to do. You want it to
write the _value_ of rua, and the int. But that's not what it's doing.

If you check sizeof(string) you will find that it is always the same
regardless of the contents. On my machine, that's 4 bytes, probably
because it is only a wrapper around a pointer.

So when you do this:

fwrite(&meu, sizeof(ender), 1, fp);

you are only writing a pointer ("rua") and the int ("n"). You're not
writing the string that "rua" is pointing to.

Depending on your inputs, that pointer stored by "rua" might not
actually change. So you write out the same pointer 3 times. Then you
read that same pointer back 3 times and they all point to the same
thing: the last string you've read from the keyboard.

You really need a way to serialize the object (hint: Google "serialize
C++") and a way to recover it. Doing it the way you have is a bad idea
-- as you've seen it doesn't work.

--Jonathan

Rafael Anschau

unread,
Jul 30, 2009, 8:03:22 PM7/30/09
to
Actually the problem was I had to refine my definition for feof: It
only returns trues after it tries to pass eof, not when it reaches it.

But thanks anyway,

Rafael

Jerry Coffin

unread,
Jul 30, 2009, 8:06:32 PM7/30/09
to
In article <6ff1c26e-5bf9-4092-86aa-d021f3e310e3
@h30g2000vbr.googlegroups.com>, rafael....@gmail.com says...

[ ... ]

> while(!feof(fp)) {

Your problem is right here. The problem is that eof on a stream isn't
detected until AFTER you attempt a read and you're at the end of the
end of the file, and the attempted read fails. When the read fails at
the end of the file, what was previously read will still be there,
but you won't have detected the end of the file yet, so you'll
process it again. Then the code above will detect that the previous
read failed and quit trying to read any more.

--
Later,
Jerry.

red floyd

unread,
Jul 30, 2009, 11:29:33 PM7/30/09
to

James Kanze

unread,
Jul 31, 2009, 5:13:10 AM7/31/09
to
On Jul 31, 2:03 am, Rafael Anschau <rafael.ansc...@gmail.com> wrote:
> Actually the problem was I had to refine my definition for
> feof: It only returns trues after it tries to pass eof, not
> when it reaches it.

That's certainly one problem, but Jonathan is also right. You
can't use fread and fwrite (nor istream::read and
istream::write) on the structure you've defined. If it works,
it's only by chance. In general, binary dumps of anything but
char only work within the same process---you can't reread the
data from code compiled with a different compiler, or sometimes
different version of the same compiler or with different
compiler options. And even within the same process, you're
restricted to PODs without pointers.

--
James Kanze (GABI Software) email:james...@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Rafael Anschau

unread,
Jul 31, 2009, 3:42:33 PM7/31/09
to
"Yes, but that's not actually what you want it to do. You want it to
write the _value_ of rua, and the int. But that's not what it's doing.
"

How come ? I can read the file after I quit and come back(I just need
to set open to a+ for instance) If the data is not there, then where
is it ?

Thanks,

Rafael


On Jul 30, 8:54 pm, Jonathan Lee <cho...@shaw.ca> wrote:

Jonathan Lee

unread,
Jul 31, 2009, 5:42:01 PM7/31/09
to
On Jul 31, 3:42 pm, Rafael Anschau <rafael.ansc...@gmail.com> wrote:
> > "Yes, but that's not actually what you want it to do. You want it to
> > write the _value_ of rua, and the int. But that's not what it's doing.
> How come ? I can read the file after I quit and come back(I just need
> to set open to a+ for instance) If the data is not there, then where
> is it ?

First I recommend trying this: add the following line before
you write to the file:

cout << "Writing " << sizeof(ender) << " bytes" << endl;

It will print the same thing for every record, on my system that's
8 bytes. It's 8 bytes if I enter a 2 letter string for the street
name, and it's 8 bytes if I enter 40 letters for the street name.
When it's done, "1.tst" is always 24 bytes. So if I've entered
three 40 letter names, how could the data possibly be there?

Now to answer your question. What fwrite is doing is writing
the 8 bytes (sizeof(ender)) located at the address &meu. Of course,
this contains an ender structure, which is roughly

struct ender {
string rua;
int n;
}

Now rua, the string object, is just a pointer to a char* buffer
(a bit of a simplification, but good enough for this discussion).
So in reality you have this:

struct ender {
char* rua; // the only data a string object "contains"
int n;
}

And rua only contains a pointer to the actual string data. So
this is what you get when you write it out to the file.

As a concrete example, suppose I type in the street name as
"ReallyLongStreetName" and the number as 9. When you read the
value into rua, it will allocate a char* to hold the data and
store that address inside the string object. Let's say that
address is 0x40302010. Then 'meu' will hold

0x40302010 // string rua;
0x00000009 // int n;

When you write out the file you will get something like

0x40, 0x30, 0x20, 0x10, 0x00, 0x00, 0x00, 0x09

in 1st.tst, which has nothing to do with "ReallyLongStreetName"

When you read it back you get the same address back and it
just happens to contain the same string, leading you to
think that it's working, when it isn't.

What you need to do is serialize the object, as I said before.
One way to do this would be:

write the string length as a number
write the C-string value of rua, pointed to by rua.c_str()
write the street number

One final note: writing numbers is not portable. There is the
matter of "endianness", and also sizeof(int). But maybe convince
yourself of the above first...

--Jonathan

0 new messages