C++Talk.NET Forum Index C++Talk.NET
C++ language newsgroups
 
Archives   FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Casting basic types - well-defined or undefined behaviour?

 
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ Language (Moderated)
View previous topic :: View next topic  
Author Message
Earl Purple
Guest





PostPosted: Fri Aug 11, 2006 5:13 pm    Post subject: Casting basic types - well-defined or undefined behaviour? Reply with quote



Please tell me if this is well-defined behaviour.

#include <cstdio>
#include <iostream>

int main()
{
double d = 500000.0;
unsigned char buf[ sizeof( double ) ];
std::memcpy( buf, &d, sizeof( double ) );

// now:
const double * pd = reinterpret_cast< const double * >( buf );
std::cout << "double value is " << *pd << '\n';
}

Basically, is dereferencing pd safe behaviour?

On one particular compiler in release mode with high optimisation, I
got "Bus error (core dumped)" on that line (in a similar situation).
The following did work:

int main()
{
double d = 500000.0;
unsigned char buf[ sizeof( double ) ];
std::memcpy( buf, &d, sizeof( double ) );

// now:
double d2;
std::memcpy( reinterpret_cast< unsigned char * >(&d2), buf, sizeof(
double ) );
std::cout << "double value is " << d2 << '\n';
}

The above isn't exactly what I was doing. Thus the reinterpret_cast to
unsigned char * which isn't necessary for memcpy but is for my code
(which is more typesafe) but ends up calling memcpy anyway.


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
peter koch larsen
Guest





PostPosted: Fri Aug 11, 2006 7:58 pm    Post subject: Re: Casting basic types - well-defined or undefined behaviou Reply with quote



Earl Purple wrote:
Quote:
Please tell me if this is well-defined behaviour.

#include <cstdio
#include <iostream

int main()
{
double d = 500000.0;
unsigned char buf[ sizeof( double ) ];
std::memcpy( buf, &d, sizeof( double ) );

// now:
const double * pd = reinterpret_cast< const double * >( buf );
std::cout << "double value is " << *pd << '\n';
}

Basically, is dereferencing pd safe behaviour?

[snip]

I do not believe so. The problem is that buf is not guaranteed to be
aligned correctly (and probably that is what you saw in the text I
snipped Wink).
You can use the alignment trick recommended by Andrei Alexandrescu
which is something like this:

union buf
{
char buf[mytype_size];
double _private;
void (*_private2)();
// more built-in types here
};

and use this one instead of your character-only buffer. While not
guaranteed to be portable, Andrei (and I) doubt there is or ever will
be a compiler where using that trick will be nonportable.

/Peter


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Frederick Gotham
Guest





PostPosted: Fri Aug 11, 2006 8:03 pm    Post subject: Re: Casting basic types - well-defined or undefined behaviou Reply with quote



Earl Purple posted:

Quote:
Please tell me if this is well-defined behaviour.

#include <cstdio
#include <iostream

int main()
{
double d = 500000.0;
unsigned char buf[ sizeof( double ) ];
std::memcpy( buf, &d, sizeof( double ) );


I would advocate use of the following idiom:

memcpy(buf,&d,sizeof d);


Quote:
// now:
const double * pd = reinterpret_cast< const double * >( buf );
std::cout << "double value is " << *pd << '\n';
}

Basically, is dereferencing pd safe behaviour?


Your code is absolutely fine... except for one small issue:

The alignment of your char array might not be suitably aligned.

This will result in undefined behaviour. You need to make sure the buffer
is suitably aligned. Two ways of doing so:

(1) Use dynamic memory allocation (which always yields strictly aligned
memory).

(2) Use boost::aligned_storage.

Here's an example which uses dynamic allocation:

#include <cstdlib>
#include <iostream>

using std::memcpy;
using std::cout;

int main()
{
double d = 50000.0;

char unsigned (&arr)[sizeof d] = *new char unsigned[1][sizeof d];

memcpy(arr,&d,sizeof d);

double const *const p = reinterpret_cast<double const*>(arr+0);

cout << *p;

delete [] &arr;
}


Quote:
On one particular compiler in release mode with high optimisation, I
got "Bus error (core dumped)" on that line (in a similar situation).
The following did work:


Because you copy in into suitably aligned memory (i.e. a double) before
accessing it as a double.


Quote:
int main()
{
double d = 500000.0;
unsigned char buf[ sizeof( double ) ];
std::memcpy( buf, &d, sizeof( double ) );

// now:
double d2;
std::memcpy( reinterpret_cast< unsigned char * >(&d2), buf, sizeof(
double ) );
std::cout << "double value is " << d2 << '\n';
}

The above isn't exactly what I was doing. Thus the reinterpret_cast to
unsigned char * which isn't necessary for memcpy but is for my code
(which is more typesafe) but ends up calling memcpy anyway.

--

Frederick Gotham

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Maciej Sobczak
Guest





PostPosted: Fri Aug 11, 2006 10:44 pm    Post subject: Re: Casting basic types - well-defined or undefined behaviou Reply with quote

Earl Purple wrote:

Quote:
double d = 500000.0;
unsigned char buf[ sizeof( double ) ];
std::memcpy( buf, &d, sizeof( double ) );

// now:
const double * pd = reinterpret_cast< const double * >( buf );
std::cout << "double value is " << *pd << '\n';

Basically, is dereferencing pd safe behaviour?

No, because it's not guaranteed that buf will be correctly aligned to be
read as double.
For example, if sizeof(double) == 8 on some platform, then it might be
also possible that this platform expect that double will be stored at
addresses that are divisible by 8. It's not the C++ requirement - but it
might be a hardware requirement.

Quote:
On one particular compiler in release mode with high optimisation, I
got "Bus error (core dumped)" on that line (in a similar situation).

Which was most likely caused by misaligned read.

Quote:
The following did work:

int main()
{
double d = 500000.0;
unsigned char buf[ sizeof( double ) ];
std::memcpy( buf, &d, sizeof( double ) );

// now:
double d2;
std::memcpy( reinterpret_cast< unsigned char * >(&d2), buf, sizeof(
double ) );
std::cout << "double value is " << d2 << '\n';

The above works, because the data is always read from the memory
location that has the correct alignment with respect to the given type.

There are various dirty tricks to get the correct alignment. The most
popular is this:

union
{
char buf[sizeof(double)];
double dummy;
};

The whole union gets the alignment of the most restricted member - in
this case, it's the dummy variable.


--
Maciej Sobczak : http://www.msobczak.com/
Programming : http://www.msobczak.com/prog/

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Earl Purple
Guest





PostPosted: Mon Aug 14, 2006 5:51 pm    Post subject: Re: Casting basic types - well-defined or undefined behaviou Reply with quote

thank you all for the responses:

Frederick Gotham wrote:

Quote:
This will result in undefined behaviour. You need to make sure the buffer
is suitably aligned. Two ways of doing so:

(1) Use dynamic memory allocation (which always yields strictly
aligned
memory).

Not really an option here. The buffer is in my portable_basic_string
class which uses an internal buffer when the size is less than 16. As
in this case it is (the size is Cool there is no dynamic allocation. I'm
not planning to modify the portable_basic_string class.

Quote:
(2) Use boost::aligned_storage.

which does it how? (I know, I could look it up. But as this is
comp.lang.c++.moderated I prefer if one gave answers as to how to do
something inside the language, not using a 3rd party library, even
boost).

Anyway, I have a way now that is well-defined that does not use dynamic
allocation and which should probably be efficient enough so I will
stick with it.

It's interesting how many pointed out that use of a union would be
portable, when I have been told that writing to one member then reading
from another is technically undefined behaviour (even if most compilers
would allow it).


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Bo Persson
Guest





PostPosted: Mon Aug 14, 2006 6:55 pm    Post subject: Re: Casting basic types - well-defined or undefined behaviou Reply with quote

"Earl Purple" <earlpurple (AT) gmail (DOT) com> skrev i meddelandet
news:1155547143.388158.128720 (AT) b28g2000cwb (DOT) googlegroups.com...
Quote:
thank you all for the responses:

Frederick Gotham wrote:

This will result in undefined behaviour. You need to make sure the
buffer
is suitably aligned. Two ways of doing so:

(1) Use dynamic memory allocation (which always yields strictly
aligned
memory).

Not really an option here. The buffer is in my portable_basic_string
class which uses an internal buffer when the size is less than 16.
As
in this case it is (the size is Cool there is no dynamic allocation.
I'm
not planning to modify the portable_basic_string class.


If you are copying string data, there will be no problem as a char has
the smallest alignment requirements.

On the other hand, if you are really storing a double in your
portable_basic_string, you *are* in trouble. :-)


Bo Persson



[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Maciej Sobczak
Guest





PostPosted: Mon Aug 14, 2006 6:55 pm    Post subject: Re: Casting basic types - well-defined or undefined behaviou Reply with quote

Earl Purple wrote:

Quote:
It's interesting how many pointed out that use of a union would be
portable, when I have been told that writing to one member then reading
from another is technically undefined behaviour

Actually, nobody suggested it (at least at the time I'm writing this).

The union is usually used not for writing and reading from different
field, but to ensure correct alignment for subsequent use of memcpy.
This is portable.

Something like this:

union
{
char buf[sizeof(double)];
double dummy;
} u;

double d1 = 3.14159;
memcpy(u.buf, &d1, sizeof(d1));

double d2 = *reinterpret_cast<double*>(u.buf);

The above is OK and the union was not (ab)used in any way - apart from
tricking the compiler into aligning the char[] buffer at least as
restrictvely as it would do so for a regular double.

Note that the dummy member was never used here.


--
Maciej Sobczak : http://www.msobczak.com/
Programming : http://www.msobczak.com/prog/

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
johnchx2@yahoo.com
Guest





PostPosted: Mon Aug 14, 2006 9:45 pm    Post subject: Re: Casting basic types - well-defined or undefined behaviou Reply with quote

Earl Purple wrote:
Quote:
Frederick Gotham wrote:
(2) Use boost::aligned_storage.

which does it how? (I know, I could look it up. But as this is
comp.lang.c++.moderated I prefer if one gave answers as to how to do
something inside the language, not using a 3rd party library, even
boost).

Perhaps tr1::aligned_storage, then. (Not quite in the language
yet...but soon!)

Under the hood, boost::aligned_storage is implemented using the "union
of char[] and aligner_type" trick described elsewhere.
tr1::aligned_storage is likely to be implemented in more or less the
same way.


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Frederick Gotham
Guest





PostPosted: Mon Aug 14, 2006 9:46 pm    Post subject: Re: Casting basic types - well-defined or undefined behaviou Reply with quote

Earl Purple posted:

Quote:
(2) Use boost::aligned_storage.

which does it how?


Check out this post which makes use of it:

http://groups.google.ie/group/comp.std.c++/msg/a82d4cddc40c08d2?hl=en&

--

Frederick Gotham

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Display posts from previous:   
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ Language (Moderated) All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2006 phpBB Group
SEO toolkit © 2004-2006 webmedic.