A pointer to a member in C ++ can be understood as a definition of "offset". Although there are some difficulties when you have virtual functions, etc., But in your case it is good enough.
So basically your code is similar (if we convert data types to raw data)
int extract(void *p, int offset) { return *((int*)(p+offset)); }
(technically speaking, this code does not compile because the void * pointer cannot be used in an additional expression, since the compiler does not know the size of the pointer, but ignore it for now.)
and when you call it, the code looks like
extract(p, __offsetof(Person, id));
where __offsetof is the pseudo- __offsetof that the compiler will compute at compile time.
You can see that there is no magic. What C ++ helps you with is that the offset is protected in a special data type, so you are not allowed to change its internal and therefore avoid breaks.
Additional information on casting and arithmetic of a pointer to C / C ++
The above code is quite simple for C / C ++ users, although this is not the form that a C ++ specialist would like to see (should use static_cast or reinterpret_cast instead of C stype casting).
I see that the question asked about further explanations of the arithmetic of pointers to C / C ++, and this is a bit off topic, since it has nothing to do with the question asked here, so instead I give an additional link to this question.
Pointer Arithmetic
http://www.technoplaza.net/programming/lesson9p2.php
http://www.informit.com/articles/article.aspx?p=686170&seqNum=8