Search code examples
c++referencevoid-pointersreinterpret-caststrict-aliasing

Why strange behavior with casting back pointer to the original class?


Assume that in my code I have to store a void* as data member and typecast it back to the original class pointer when needed. To test its reliability, I wrote a test program (linux ubuntu 4.4.1 g++ -04 -Wall) and I was shocked to see the behavior.

struct A
{
  int i;
  static int c;
  A () : i(c++) { cout<<"A() : i("<<i<<")\n"; }
};
int A::c;

int main ()
{
  void *p = new A[3];  // good behavior for A* p = new A[3];
  cout<<"p->i = "<<((A*)p)->i<<endl;
  ((A*&)p)++;
  cout<<"p->i = "<<((A*)p)->i<<endl;
  ((A*&)p)++;
  cout<<"p->i = "<<((A*)p)->i<<endl;
}

This is just a test program; in actual for my case, it's mandatory to store any pointer as void* and then cast it back to the actual pointer (with help of template). So let's not worry about that part. The output of the above code is,

p->i = 0
p->i = 0 // ?? why not 1
p->i = 1

However if you change the void* p; to A* p; it gives expected behavior. WHY ?

Another question, I cannot get away with (A*&) otherwise I cannot use operator ++; but it also gives warning as, dereferencing type-punned pointer will break strict-aliasing rules. Is there any decent way to overcome warning ?


Solution

  • Well, as the compiler warns you, you are violating the strict aliasing rule, which formally means that the results are undefined.

    You can eliminate the strict aliasing violation by using a function template for the increment:

    template<typename T>
    void advance_pointer_as(void*& p, int n = 1) {
        T* p_a(static_cast<T*>(p));
        p_a += n;
        p = p_a;
    }
    

    With this function template, the following definition of main() yields the expected results on the Ideone compiler (and emits no warnings):

    int main()
    {
        void* p = new A[3];
        std::cout << "p->i = " << static_cast<A*>(p)->i << std::endl;
        advance_pointer_as<A>(p);
        std::cout << "p->i = " << static_cast<A*>(p)->i << std::endl;
        advance_pointer_as<A>(p);
        std::cout << "p->i = " << static_cast<A*>(p)->i << std::endl;
    }