Is a.b always (&a)->b?

In C, a.b is normally synonymous with (&a)->b.

There is nothing in the C11 standard that mandates this equivalence under all circumstances. The relevant section (5.5.2.3 Structure and union members) actually deals with them distinctly and the footnote in that section indicates where the misapprehension arises:

If &E is a valid pointer expression (where & is the "address-of" operator, which generates a pointer to its operand), the expression (&E)->MOS is the same as E.MOS.

In other words, &E has to be valid for this equivalence to hold. One place where it doesn't hold is:

#include <stdio.h>

struct xyzzy { int plugh; } twisty;
struct xyzzy getTwistyCopy(void) { return twisty; }

int main(void) {
    twisty.plugh = 42;
    printf("%d\n", (   getTwistyCopy()  ).plugh);
    printf("%d\n", ( &(getTwistyCopy()) )->plugh);
}

The first printf line is fine but the second is not. That's really because you cannot take the address of a function return value. But you can still see, for an arbitrary a, that a.b and (&a)->b are not always identical.


Here are three counterexamples, all based on constraints on applying &:

  • a is an rvalue because it is a structure returned by a function:
    int bar(void)
    {
        extern struct S { int b; } foo(void);
        return (&foo())->b;
    }
    
    Clang says “error: cannot take the address of an rvalue of type 'struct S'”. But it accepts return foo().b;.
  • a is an rvalue because it is the result of an assignment:
      int bar(void)
      {
          struct S { int b; } x = {0};
          struct S y;
          return (&(y=x))->b;
      }
    
    
    Clang says “error: cannot take the address of an rvalue of type 'struct S'”. But it accepts return (y=x).b;.
  • a is declared with register, so its address may not be taken:
    int bar(void)
    {
        register struct S { int b; } a = {0};
        return (&a)->b;
    }
    
    Clang says “error: address of register variable requested”.

In a.b, a is not required to be an lvalue.

For example, if a is a macro that expands to a function call, then (&a)->b is a constraint violation.