问题
Accessing objects via reinterpret_cast
ed pointers and related UB has been extensively discussed here. After reading questions and answers, I'm still not sure about proper using uninitialized memory with POD types.
Suppose I want to "emulate"
struct { double d; int i; };
by manually allocating memory for data members and suppose (for simplicity) that no padding is needed before i
.
Now, I do this:
// (V1)
auto buff = reinterpret_cast<char*>(std::malloc(sizeof(double) + sizeof(int)));
auto d_ptr = reinterpret_cast<double*>(buff);
auto i_ptr = reinterpret_cast<int*>(buff + sizeof(double));
*d_ptr = 20.19;
*i_ptr = 2019;
First question: is this code valid?
I could use placement new
:
// (V2)
auto buff = reinterpret_cast<char*>(std::malloc(sizeof(double) + sizeof(int)));
auto d_ptr = new(buff) double;
auto i_ptr = new(buff + sizeof(double)) int;
*d_ptr = 20.19;
*i_ptr = 2019;
Do I have to? Placement new
seems to be redundant here because default initialization of POD types is no-op (vacuous initialization), and [basic.life] reads:
The lifetime of an object of type
T
begins when:(1.1) storage with the proper alignment and size for type
T
is obtained,(1.2) if the object has non-vacuous initialization, its initialization is complete, ...
Does this say that the lifetime of *d_ptr
and *i_ptr
objects began once I had allocated memory for them?
Second question: can I use type double*
(or some T*
) for buff
, i.e.
// (V3)
auto buff = reinterpret_cast<double*>(std::malloc(sizeof(double) + sizeof(int)));
auto d_ptr = reinterpret_cast<double*>(buff);
auto i_ptr = reinterpret_cast<int*>(buff + 1);
*d_ptr = 20.19;
*i_ptr = 2019;
or
// (V4)
auto buff = reinterpret_cast<double*>(std::malloc(sizeof(double) + sizeof(int)));
auto d_ptr = new(buff) double;
auto i_ptr = new(buff + 1) int;
*d_ptr = 20.19;
*i_ptr = 2019;
?
回答1:
As Barry states better here, 1&3 are UB. The short version: none of those pieces of code contain any of the syntax needed to create an object. And you can't access the value of an object that isn't there.
So, do 2 and 4 work?
#2 works if and only if alignof(double) >= alignof(int)
. But it only works in the sense that it create a double
followed by an int
. It does not in any way "emulate" that nameless struct. The struct could have any arbitrary amount of padding, while in this case, the int
will immediately follow the double
.
#4 does not work, strictly speaking. buff
does not actually point to the newly created double
. As such, pointer arithmetic cannot be used to get the byte after that object. So doing pointer arithmetic yields undefined behavior.
Now, we are talking about C++ strictly speaking. In all likelihood, every compiler will execute all four of these (with the above caveat about alignment).
回答2:
When I look at publicly available draft, http://eel.is/c++draft/basic.life the quote is different, and it says that
The lifetime of an object of type T begins when:
(1.1) storage with the proper alignment and size for type T is obtained, and
(1.2) its initialization (if any) is complete (including vacuous initialization) ([dcl.init]),
Since there was no vacuous
initialization of the double variable, I believe the code is incorrect and invokes undefined behavior.
来源:https://stackoverflow.com/questions/56450444/object-access-using-reinterpret-cast-for-struct-double-int-like-object