问题
Accessing objects via reinterpret_casted 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
Tbegins when:(1.1) storage with the proper alignment and size for type
Tis 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