Ok, first of I noticed the code snippet you provided used void main(). This is no longer standards compliant as I am sure you know but to allow myself to sleep tonight I feel obligated to post the link on how main() should be defined.
Second, my disclaimer: The following discussion involves code that is not standards compliant. According to ISO standard you cannot dereference a NULL pointer or perform some of these variable aliases. This results in undefined behavior which means the results vary from implementation to implementation.
Third, sometimes things like this are useful and have been used in OS development where the developers are allowed to make assumptions on how the code will react. I mean, h**l even John Carmack used goto.
So let's take a look at that first piece of code:
The first part is the easy part, we will cast the result to type char* which is the type of actual memory addresses. Now the second part:
offset = (char *) & ((struct c *) (&((struct a *)0) -> y) -> j) - (char *)((struct a*)0);
The ((struct a*)0) performs a trick which returns the base address for struct a. Having an address of a is the same as a pointer to 'a' so to access the members of object 'a' with ptr 'a' you would use pointer notation e.g. a->y->j. Then we obtain the address of j with &j.
(struct c *) (&((struct a *)0) -> y) -> j)
The next part of the line:
Performs the same trick that we talked about above, e.g. it returns the base address of a. So now for offset we are performing this calculation:
(char *)((struct a*)0);
This gives us the number of bytes from the start of struct 'a' that the j member is located.
offset = (address of j) - (address of a)
Now on to the next piece:
With the above knowledge this is a little easier to tackle. So we are tacking the actual memory address of member j with this line &(p->j). The char* we discussed above.
address = (struct b *)((char *) & (p -> j) - offset);
So with the actual memory address of j we are subtracting offset which as we discussed is the number of bytes from the begginning of the struct. So we are doing:
Since math works, we now have the actual memory address of the first thing in struct 'a', which if you look at the definition for the struct you see that the first thing in struct 'a' is struct b.
address = (address of j) - (number of bytes into the structure j is)
So now we have the memory address of struct b, which is the same as having a ptr to struct b. *note we casted the memory address to pointer type struct b above so the compiler would know about the members*
So with that ptr we can assign values the way you would do pointers, e.g.
That about sums it up, let me know if you have any questions.