Object access using reinterpret_cast for “struct {double, int}”-like object

泄露秘密 提交于 2020-01-04 05:21:09

问题


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 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!