Reinterpret_cast <char *> (p) or static_cast <char *> ((void *) p)) for different pointer differences, which is better?

Is there a difference between the following three casts to extract the original byte pointers for use in pointer arithmetic? (Assume the platform, where char is 1 byte.)

  • static_cast<char*>((void*)ptr))
  • reinterpret_cast<char*>(ptr)
  • (updated) or: static_cast<char*>(static_cast<void*>(ptr))

What should I prefer?

More details ...

Given the pointers to two member objects in the class, I would like to calculate the offset from one to the other so that I can recover the address of one member, given the offset and address of the other member.

 // assumed data layout: struct C { // ... A a; // ... B b; } 

The code I'm currently using corresponds to the lines:

 void approach1( A *pa, B *pb ) { // compute offset: std::ptrdiff_t offset = static_cast<char*>((void*)pa) - static_cast<char*>((void*)pb); // then in some other function... // given offset and ptr to b, compute ptr to a: A *a = static_cast<A*>( (void*)(static_cast<char*>((void*)pb) + offset) ); } main() { C c; approach1(&c.a, &c.b); } 

I would like to know if the following is better (or worse):

 void approach2( A *pa, B *pb ) { std::ptrdiff_t offset = reinterpret_cast<char*>(pa) - reinterpret_cast<char*>(pb); // ... A *a = reinterpret_cast<A*>( reinterpret_cast<char*>(pb) + offset ); } 

Are these two methods fully equivalent? Are they equally portable?

My impression is that approach1() more portable because the static_cast pointer to and from void* saves the address ", while reinterpret_cast<> guarantees less (see the accepted answer by reference).

I would like to know what is the cleanest way to do this.

Update: Target Explanation

Many people ask what the purpose of calculating these offsets is. The goal is to build a table of metaclasses of instance offsets. This is used by the runtime reflection engine to automatically create GUIs and resilience (offsets are not serialized, they are just used to move around the structure). The code has been working for over 15 years. For the purposes of this question, I just want to know the most portable way to calculate pointer offsets. I'm not going to make big changes to the way the metaclass system works. Also, I'm usually interested in the best way to do this, since I have other use cases (like difference pointers for shared memory code).

NOTE: I cannot use offsetof() , because in my actual code I only have pointers to instances a and b , I do not have to have the type containing the object c or other static information to use offsetof() . All I can assume is that a and b are members of the same object.

+6
source share
2 answers

These two will lead to the same result, so the difference is mostly semantic, and reinterpret_cast has exactly the meaning of the operation you want, plus the fact that instead of two, you only need one actor instead of two (and all the less you have to throw in your code , all the better).

reinterpret_cast

5.2.10 / 7 : an object pointer can be explicitly converted to an object pointer of another type. When prvue v of the object pointer type is converted to the cv T pointer object type, the result is static_cast <cv T *> (static_cast <cv void *> (v)).

Therefore, if an exotic low-level random behavior appears on a middle-aged platform, you should definitely:

 reinterpret_cast<char*>(ptr); 

Generally.

However, why don't you use uintptr_t in your case? this is even more appropriate, you do not need a pointer:

 void approach3( A *pa, B *pb ) { std::ptrdiff_t offset = reinterpret_cast<std::uintptr_t>(pa) - reinterpret_cast<std::uintptr_t>(pb); // ... A *a = reinterpret_cast<A*>( reinterpret_cast<std::uintptr_t>(pb) + offset ); } 

For more information, see

http://en.cppreference.com/w/cpp/language/reinterpret_cast

+6
source

I do not recommend calculating distance offsets between member addresses. Either the compiler can enter add-on data, or even if it works, it will work the same way only for that particular compiler running on that particular host. There are many sources of error in applying this practice. For example, what if you have to deal with known virtual tables and memory layout in several virtual inheritances ? This will completely make your decision unusable.

So back to the roots: why are you trying to do this? Perhaps there is a better solution.

EDIT / Update

Thank you for explaining the reason. This is a very interesting approach that I have not seen so far. Today I learned something.

However, I still stick to my point of view that there should be a much simpler way to deal with this. And just like the concept of proof, I wrote a small application to see which of your methods works. None of them work for me.

The application is a somewhat advanced one of your methods, here it is:

 #include <iostream> #include <stdio.h> #include <string> struct A { A(const std::string& pa) : a(pa) {printf("CTR: A address: %p\n", this) ;} std::string a; }; struct B { B(const std::string& pb) : b(pb) {printf("CTR: B address: %p\n", this) ;} std::string b; }; // assumed data layout: struct C { C() : a("astring"), b("bstring") {} // ... A a; // ... B b; }; void approach1( A *pa, B *pb ) { printf("approach1: A address: %p B address: %p\n", pa, pb); // compute offset: std::ptrdiff_t offset = static_cast<char*>((void*)pb) - static_cast<char*>((void*)pa); // then in some other function... // given offset and ptr to b, compute ptr to a: A *a = static_cast<A*>( (void*)(static_cast<char*>((void*)pb) + offset) ); printf("approach1: a address: %p \n", a); std::cout << "approach1: A->a=" << a->a << std::endl; } void approach2( A *pa, B *pb ) { printf("approach2: A address: %p B address: %p\n", pa, pb); std::ptrdiff_t offset = reinterpret_cast<char*>(pb) - reinterpret_cast<char*>(pa); A *a = reinterpret_cast<A*>( reinterpret_cast<char*>(pb) + offset ); printf("approach2: a address: %p \n", a); std::cout << "approach2: A->a=" << a->a << std::endl; } main() { C c; std::cout << caa << std::endl; approach1(&c.a, &c.b); approach2(&c.a, &c.b); } 

Output it on my computer ( uname -a Linux flood 3.13.0-33-generic #58-Ubuntu SMP Tue Jul 29 16:45:05 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux ) with my compiler ( g++ (Ubuntu 4.8.2-19ubuntu1) 4.8.2 ):

 CTR: A address: 0x7fff249f0900 CTR: B address: 0x7fff249f0908 astring approach1: A address: 0x7fff249f0900 B address: 0x7fff249f0908 approach1: a address: 0x7fff249f0910 approach1: A->a=<GARBAGE> approach2: a address: 0x7fff249f0910 

where <GARBAGE> , as expected, contains ... garbage.

See below: http://ideone.com/U8ahAL

0
source

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


All Articles