I don't follow. How can it "pretend" that a struct c* is at location 0?
|
Casting. There is nothing "sacred" about 0. It is invalid to use zero as a memory location for reading, but as was pointed out earlier, the expressions being used do not read from the structure, so the location zero is never used.
Think of it this way. Let's say you have a C;
1 2 3 4 5 6 7
|
C c;
auto cptr = &c;
auto bptr = &(c.b);
auto vect_to_b = (char *)bptr - (char *)cptr;
|
What do you get with vect_to_b? It is the distance in bytes from the beginning of c (by its address) to the address of the member b (by its address)
Now, in any of that, did the compiler generate code which reads from "c" or "b". No. It didn't. It does, however, happen that c is a valid instance
in this example, not located at zero.
But what would it have mattered? Since it did not read from the instance, all it calculated was the distance between two positions in memory.
It would not matter where "c" is located. It's memory location has no effect on the resulting calculation. vect_to_b is only calculating the distance between the two positions.
That is why the code you're asking about works, because the location of "c" would not matter, and so "zero" is as good as any choice to use for code that performs the subtraction you see in my example.
However, the example you posted does not perform a subtraction from two positions. It only shows the distance to the member "b". Consider what you get from the two addresses in my example, cptr and bptr. What are they? The first is where "c" was formed, and the second - this is the key part here - the second (which is "bptr"), will be at some distance away from "cptr". The subtraction gives you how far.
Now, what if "c" formed at zero. Just suppose. If it had, then &cptr would be zero. What would bptr be? Zero plus the distance to "b". What would the operation producing vect_to_b do? It would subtract
zero from bptr, which is otherwise a useless task that can be skipped because "c" started at zero.
That's what your code expression does. It skips the useless subtraction because it formed a "fake" or "supposed" instance
at zero, which that code intends never to dereference or access. It is only using the address calculation to get an offset, a distance. It uses the same idea as the example I posted, knowing it can skip the subtraction because it would be a subtraction of zero from the member address.
look up "offsetof"..it can be used like this:
int x = offsetof( C, b );
It gives you the same answer as the code you've been asking about.
Oh, and I think it's a "C" macro, not a C++ macro...there are various ways to express this concept.
There is also this curious option called a pointer-to-member operator:
1 2 3 4 5
|
auto mptr = &C::b; // note CAP C - the class type, not "c", the instance
C c;
c.*mptr = 0;
|
If "b" is an int, that might look like:
1 2 3 4 5
|
int C::*mptr = &C::b;
C c;
c.*mptr = 0;
|
Which, of course, could give us the address:
auto bptr = &( c.*mptr );
or maybe something like
1 2 3 4 5
|
C *c = nullptr; // for clarity that c is zero
auto mptr = &C::b;
auto offset = &(c->*mptr);
|
Which you may, again, find maddening :)
Note, too, that "mptr" is in a class of "special" pointers that may not actually be the size of "common" pointers. They are implementation specific, which means they work on various compilers, but may look quite different on each compiler.
Also, to be clear, "mptr" is a pointer type known as a pointer to member. It is from a "class" perspective, not the perspective of an instance (hence the way it is assigned above using "C", not "c").
Its use in "c.*mptr" and "c->*mptr" above is what's known as the "pointer to member operator", where the .* and ->* has a "special" meaning, an operator of a specific kind applicable only to such pointers. What appears before the ".*" and "->*" provides an instance upon which the operator can be applied.