a.b 总是 (&a)->b 吗?

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

在 C 中,a.b 通常与 (&a)->b 同义。

这是否在所有情况下都是正确的,即使 a 是一些混乱的复合术语的宏?或者在任何版本的 C 中是否存在等效性不成立的任何边缘情况?

a.b 中,a 不需要是左值。

例如,如果 a 是一个扩展为函数调用的宏,那么 (&a)->b 就是约束违规。

这里有三个反例,都是基于应用&:

的约束条件
  • a 是右值,因为它是函数返回的结构:
    int bar(void)
    {
        extern struct S { int b; } foo(void);
        return (&foo())->b;
    }
    
    Clang 说“错误:无法获取 'struct S' 类型右值的地址”。但它接受 return foo().b;.
  • a 是右值,因为它是赋值的结果:
      int bar(void)
      {
          struct S { int b; } x = {0};
          struct S y;
          return (&(y=x))->b;
      }
    
    
    Clang 说“错误:无法获取 'struct S' 类型右值的地址”。但它接受 return (y=x).b;.
  • a是用register声明的,所以它的地址可能不会被占用:
    int bar(void)
    {
        register struct S { int b; } a = {0};
        return (&a)->b;
    }
    
    Clang 说“错误:请求的寄存器变量地址”。

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

C11 标准中没有任何内容要求 在所有情况下都具有这种等效性。相关部分(5.5.2.3 Structure and union members)实际上对它们进行了明确的处理,该部分的脚注指出了误解的出处:

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.

换句话说,&E 必须有效才能使这个等价成立。它的一个地方是:

#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);
}

第一行 printf 行没问题,但第二行不行。那真的是因为你不能获取函数 return 值的地址。但是您仍然可以看到,对于任意 aa.b(&a)->b 并不总是相同的。