Again, to be crystal clear, consider:
/* 1 */
a -> x = 2;
and
/* 2 */
* (& ( a -> x )) = 2;
and
/* 3 */
int* x =& a->x;
*x = 2;
You really think there's a difference? Really? Where's the difference?
Between 1 and 2, or 2 and 3? I /hope/ between 1 and 2. 2 and 3 better
be entirely equivalent, or I'm really losing it.
As a naive understanding for the difference between 1 and 2: The
addressof operator (&) simply returns the address of the object
referred to the lvalue, and then the dereference operator (*) simply
takes that pointer value and returns back the same lvalue (which
refers to the same object). This isn't operator overloading in C++. I
would think that it ought to be a noop. If there is any difference at
all between any of 1, 2, and 3 above in this post, then I have a
fundamental misunderstanding of the language.
I don't think a C pointer is simply just the address of an object. If
you consider the rules of pointer arithmetic and DR260, a pointer value
carries some extra properties that decide what operations on it are
defined. Two pointers may compare equal and be represented by identical
bit patterns, but depending on their "provenance", one of them may be
safe to dereference or increment but not decrement, while the other may
be safe to decrement but not increment or dereference. The standard
tells us that every object can be considered an array element, and every
pointer to an object has a range of integers that can be legitimately
added to it, based on the object's "arrayness"; but the standard rarely
bothers explaining how to determine what that array is, and all we can
do is rely on obvious guesses where they're obvious, and in less-obvious
cases we can hope that the guess is even harder for a compiler, forcing
it to generate code that does the "naive" thing regardless of what the
limit would be if the standard didn't neglect to specify it.
Since this "arrayness" is not exactly on topic here, I don't want to go
too deep into it now; but maybe pointers are also supposed to remember
their "structness", and your &a->x (and also your x) are not just
pointers to an int that is known not to be an array element, but
pointers to an int that is known not to be an array element but is also
known to be the "x" member of a struct T1? If that were the case, then
maybe a simple assignment to *x could still impose an effective type of
struct T1 on the object surrounding the int that x points to. But of
course none of that is actually discussed in the standard, just like the
transformations of "arrayness" are not discussed for most of the
operations where they apparently happen.