This question is about pointers derived using pointer arithmetic with struct offsets.
Consider the following program:
#include <cstddef>
#include <iostream>
#include <new>
struct A {
float a;
double b;
int c;
};
static constexpr auto off_c = offsetof(A, c);
int main() {
A * a = new A{0.0f, 0.0, 5};
char * a_storage = reinterpret_cast<char *>(a);
int * c = reinterpret_cast<int *>(a_storage + off_c));
std::cout << *c << std::endl;
delete a;
}
This program appears to work and give expected results on compilers that I tested, using default settings and C++11 standard.
(A closely related program, where we use void *
instead of char *
and static_cast
instead of reinterpret_cast
,
is not universally accepted. gcc 5.4
issues a warning about pointer arithmetic with void pointers, and clang 6.0
says that pointer
arithmetic with void *
is an error.)
Does this program have well-defined behavior according to the C++ standard?
Does the answer depend on whether the implementation has relaxed or strict pointer safety ([basic.stc.dynamic.safety]
)?
There are no fundamental errors in your code.
If A
isn't plain old data, the above is UB (prior to C++17) and conditionally supported (after C++17).
You might want to replace char*
and int*
with auto*
, but that is a style thing.
Note that pointers to members do this exact same thing in a type-safe manner. Most compilers implement a pointer to member ... as the offset of the member in the type. They do, however, work everywhere even on non-pod structures.
Aside: I don't see a guarantee that offsetof
is constexpr
in the standard. ;)
In any case, replace:
static constexpr auto off_c = offsetof(A, c);
with
static constexpr auto off_c = &A::c;
and
auto* a_storage = static_cast<char *>(a);
auto* c = reinterpret_cast<int *>(a_storage + off_c));
with
auto* c = &(a->*off_c);
to do it the C++ way.