R
REH
Someone more articulate than me please explain to Ioannis Vranos why the
following two programs both exhibit undefined behavior:
following two programs both exhibit undefined behavior:
REH said:Someone more articulate than me please explain to Ioannis Vranos why the
following two programs both exhibit undefined behavior:
#include <iostream>
#include <cstring>
class SomeClass
{
public:
double d;
int i;
float f;
long l;
};
int main()
{
using namespace std;
unsigned char array[sizeof(SomeClass)];
SomeClass obj= {1, 2, 3, 4};
memcpy(array, &obj, sizeof(obj));
SomeClass *p= reinterpret_cast<SomeClass *>(array);
cout<<p->d<<" "<<p->i<<" "<<p->f<<" "<<p->l<<"\n";
}
#include <iostream>
#include <cstring>
class SomeClass
{
public:
double d;
int i;
float f;
long l;
};
int main()
{
using namespace std;
unsigned char array[sizeof(SomeClass)];
SomeClass obj= {1, 2, 3, 4};
SomeClass *p= new(array)SomeClass(obj);
REH said:Someone more articulate than me please explain to Ioannis Vranos why the
following two programs both exhibit undefined behavior:
int main()
{
using namespace std;
unsigned char array[sizeof(SomeClass)];
SomeClass obj= {1, 2, 3, 4};
memcpy(array, &obj, sizeof(obj));
SomeClass *p= reinterpret_cast<SomeClass *>(array);
cout<<p->d<<" "<<p->i<<" "<<p->f<<" "<<p->l<<"\n";
}
int main()
{
using namespace std;
unsigned char array[sizeof(SomeClass)];
SomeClass obj= {1, 2, 3, 4};
SomeClass *p= new(array)SomeClass(obj);
Victor Bazarov said:What have you managed so far?
#include <iostream>
#include <cstring>
class SomeClass
{
public:
double d;
int i;
float f;
long l;
};
int main()
{
using namespace std;
unsigned char array[sizeof(SomeClass)];
SomeClass obj= {1, 2, 3, 4};
memcpy(array, &obj, sizeof(obj));
SomeClass *p= reinterpret_cast<SomeClass *>(array);
As I can see, 'obj' and 'array' have different alignment requirements.
Reinterpret-casting 'array' to 'obj' may violate hardware protocols for
loading values from multi-byte objects.
cout<<p->d<<" "<<p->i<<" "<<p->f<<" "<<p->l<<"\n";
}
#include <iostream>
#include <cstring>
class SomeClass
{
public:
double d;
int i;
float f;
long l;
};
int main()
{
using namespace std;
unsigned char array[sizeof(SomeClass)];
SomeClass obj= {1, 2, 3, 4};
SomeClass *p= new(array)SomeClass(obj);
Same thing here.
You could fix that if you went
unsigned char *array = new unsigned char[sizeof(SomeClass)];
and then as it was. 'new' returns pointers properly aligned.
V
int main()
{
using namespace std;
unsigned char array[sizeof(SomeClass)];
SomeClass obj= {1, 2, 3, 4};
SomeClass *p= new(array)SomeClass(obj);
Same thing here.
You could fix that if you went
unsigned char *array = new unsigned char[sizeof(SomeClass)];
and then as it was. 'new' returns pointers properly aligned.
int main()
{
char * buffer = new char[1024];
X * x = new(buffer) X; // Ok? because ..
x->~X();
}
Victor said:You could fix that if you went
unsigned char *array = new unsigned char[sizeof(SomeClass)];
and then as it was. 'new' returns pointers properly aligned.
Ioannis Vranos said:Victor said:You could fix that if you went
unsigned char *array = new unsigned char[sizeof(SomeClass)];
and then as it was. 'new' returns pointers properly aligned.
So are the following programs portable?
#include <iostream>
#include <cstring>
class SomeClass
{
public:
double d;
int i;
float f;
long l;
};
int main()
{
using namespace std;
unsigned char *parray= new unsigned char[sizeof(SomeClass)];
SomeClass obj= {1, 2, 3, 4};
memcpy(parray, &obj, sizeof(obj));
SomeClass *p= reinterpret_cast<SomeClass *>(parray);
//Continue using parray as SomeClass
cout<<p->d<<" "<<p->i<<" "<<p->f<<" "<<p->l<<"\n";
//Is this needed?
p->~SomeClass();
delete[] parray;
}
#include <iostream>
#include <cstring>
class SomeClass
{
public:
double d;
int i;
float f;
long l;
};
int main()
{
using namespace std;
unsigned char *parray= new unsigned char[sizeof(SomeClass)];
SomeClass obj= {1, 2, 3, 4};
SomeClass *p= new(parray)SomeClass(obj);
//Continue using parray as SomeClass
cout<<p->d<<" "<<p->i<<" "<<p->f<<" "<<p->l<<"\n";
//Is this needed?
p->~SomeClass();
delete[] parray;
}
Daniel said:[...]
void foo()
{
char buffer[1024];
X * x = new(buffer) X; // << undefined behaviour, because //
buffer resides on stack?
x->~X();
}
int main()
{
char * buffer = new char[1024];
X * x = new(buffer) X; // Ok? because ..
x->~X();
}
Did I got it right?
Ioannis said:Victor said:You could fix that if you went
unsigned char *array = new unsigned char[sizeof(SomeClass)];
and then as it was. 'new' returns pointers properly aligned.
So are the following programs portable?
REH said:That you cannot arbitrarily cast a char* to any other pointer, because of
alignment issues. But, I am right aren't I? This is undefined behavior.
So, even correcting for the alignment, it's still not portable? Only whenStrictly speaking, no, because they access members of the object
through a pointer that isn't a pointer to the original object.
'reinterpret_cast' is said to only preserve the object if the pointer
(and the object) on a "round-trip". This is safe:
struct SomeClass { int a; double b; char c; };
SomeClass obj = { 1,2,3 };
char *ptr = reinterpret_cast<char*>(obj); // cast to unrelated ptr
SomeClass *p = reinterpret_cast<SomeClass*>(ptr); // cast back
(p->a == 1) // yields 'true'
V
So, it is "implementation specific," not "undefined behavior." Thanks, IThomas Matthews said:behavior.
You can cast a pointer to any type. The results are implementation
(compiler) defined. I don't believe that it is undefined behavior.
Casting char * to other pointer types is commonly performed in the
embedded systems arena, but that portion of the code is platform
specific.
int main()
{
char * buffer = new char[1024];
X * x = new(buffer) X; // Ok? because ..
Not OK. You had to obtain the 'buffer' pointer using sizeof(X).
REH said:So, even correcting for the alignment, it's still not portable? Only when
your original pointer is of the correct type, portability is acheived? Is
there no way to "allocate" a block of non-heap memory, and construct an
object in it. I thought that was what placement new provided you, and then
the only concern was alignment.
Daniel said:[..]
int main()
{
char * buffer = new char[1024];
X * x = new(buffer) X; // Ok? because ..
Not OK. You had to obtain the 'buffer' pointer using sizeof(X).
would multiple of sizeof(X) also be right?
thnanks
Victor Bazarov said:REH said:So, even correcting for the alignment, it's still not portable? Only when
your original pointer is of the correct type, portability is acheived? Is
there no way to "allocate" a block of non-heap memory, and construct an
object in it. I thought that was what placement new provided you, and then
the only concern was alignment.
You thought correctly. You can do
char *ptr = new char[sizeof(SomeClass)];
and then
SomeClass *p = new (ptr) SomeClass;
which will construct the object which will have the proper alignment.
The object will have to be later destroyed using the explicit d-tor
call syntax:
p->~SomeClass();
even if the d-tor is trivial or provided by the compiler.
What you shouldn't do is
char *ptr = new char[sizeof(SomeClass)];
SomeClass *p = reinterpret_cast<SomeClass*>(ptr);
at this point '*p' is not a properly constructed object. The result
of such conversion is unspecified and therefore not portable.
V
Victor said:You thought correctly. You can do
char *ptr = new char[sizeof(SomeClass)];
and then
SomeClass *p = new (ptr) SomeClass;
which will construct the object which will have the proper alignment.
The object will have to be later destroyed using the explicit d-tor
call syntax:
p->~SomeClass();
even if the d-tor is trivial or provided by the compiler.
What you shouldn't do is
char *ptr = new char[sizeof(SomeClass)];
SomeClass *p = reinterpret_cast<SomeClass*>(ptr);
at this point '*p' is not a properly constructed object. The result
of such conversion is unspecified and therefore not portable.
Ioannis said:Victor said:You thought correctly. You can do
char *ptr = new char[sizeof(SomeClass)];
and then
SomeClass *p = new (ptr) SomeClass;
which will construct the object which will have the proper alignment.
The object will have to be later destroyed using the explicit d-tor
call syntax:
p->~SomeClass();
even if the d-tor is trivial or provided by the compiler.
What you shouldn't do is
char *ptr = new char[sizeof(SomeClass)];
SomeClass *p = reinterpret_cast<SomeClass*>(ptr);
at this point '*p' is not a properly constructed object. The result
of such conversion is unspecified and therefore not portable.
Very interesting stuff. However if you replace reinterpret_cast with:
SomeClass *p = static_cast<SomeClass *>( static_cast<void *>(ptr) );
is it guaranteed to always work?
Also, the standard says:
"For any object (other than a base-class subobject) of POD type T,
whether or not the object holds a valid value of type T, the underlying
bytes (1.7) making up the object can be copied into an array of char or
unsigned char. If the content of the array of char or unsigned char is
copied back into the object, the object shall subsequently hold its
original value".
So in strict terms it looks like that only if we copy the bytes back, we
get the same object, and making an object in our bytes is not guaranteed
to work.
The only examples I have seen in TC++PL3 for placement new, are in the
style:
int x;
int *p= new(&x) int;
Victor said:Examples are what they are, examples. It doesn't necessarily mean you
cannot use placement new with non-POD objects or for "copying".
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.