Why weird behavior with dropping a pointer to the source class?

Suppose in my code I have to store the void* element as a data element and, if necessary, return it to the original class pointer. 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 fact for my case, it is imperative to store any pointer as void* and then return it to the actual pointer (using template ). So do not worry about this part. The output of the above code ,

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

However, if you change void* p; on A* p; he will give the expected behavior . WHY?

Another question, I cannot get away with (A*&) , otherwise I cannot use operator ++ ; but it also gives a warning, because a dereferenced type-guard pointer violates strict anti-aliasing rules . Is there a decent way to overcome the warning?

+6
source share
3 answers

Well, as the compiler warns you, you are breaking a strict anti-aliasing rule, which formally means that the results are undefined.

You can eliminate the strict alias violation using the function template for incrementing:

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

Using this function template, the following main() definition gives the expected results in the Ideone compiler (and does not give any 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; } 
+11
source

You have already received the correct answer, and this is really a violation of the strict alias rule, which leads to unpredictable code behavior. I would only note that the title of your question makes a link to "drop the pointer to the source class". In fact, your code has nothing to do with back casting. Your code reinterprets the contents of the raw memory occupied by the void * pointer as the A * pointer. This is not a drop. This is a reinterpretation. Not even remotely the same.

A good way to illustrate the difference would be to use both int and float example. float value declared and initialized as

 float f = 2.0; 

the cabin will be transformed (explicitly or implicitly converted) to int type

 int i = (int) f; 

with expected result

 assert(i == 2); 

It really is a throw (conversion).

Alternatively, the same float value can also be interpreted as an int value

 int i = (int &) f; 

However, in this case, the value of i will be completely meaningless and generally unpredictable. I hope it is easy to see the difference between memory conversion and reinterpretation from these examples.

Reinterpretation is exactly what you do in your code. The expression (A *&) p is nothing more than a reinterpretation of the raw memory occupied by the void *p pointer as an A * type pointer. The language does not guarantee that these two types of pointers have the same representation and the same size. So, expecting the predictable behavior of your code will look like the expression above (int &) f will evaluate to 2 .

The proper way to drop your void * pointer should be to make (A *) p , not (A *&) p . The result of (A *) p indeed be the original value of the pointer, which can be safely manipulated using pointer arithmetic. The only right way to get the original value as an lvalue would be to use an extra variable

 A *pa = (A *) p; ... pa++; ... 

And there is no legal way to create an “in place” lvalue, as you tried to make your (A *&) p . The behavior of your code is an illustration of this.

+6
source

As others commented, your code looks as if it should work. Only once (in the 17+ years of coding in C ++) did I encounter something where I looked directly at the code, and the behavior, as in your case, simply did not make sense. I finished working with the code through the debugger and opened the disassembly window. I found something that could only be explained as an error in the VS2003 compiler, because it contained exactly one instruction. By simply rearranging the local variables at the top of the function (30 lines or so from the error), the compiler returned the correct instruction. Try a debugging debugger and follow the memory / registers to find out what it actually does?

As for the promotion of the pointer, you can promote it by following these steps:

 p = (char*)p + sizeof( A ); 

VS2003 through VS2010 never complains about it, not sure about g ++

+2
source

Source: https://habr.com/ru/post/890274/


All Articles