c++c++11pointerslanguage-lawyeroffsetof

Struct offsets and pointer safety in C++


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])?


Solution

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