25

我正在玩gcc并尝试了以下代码:

int A = 42;
int *B = &A;
int *C = &*B;

并且C == &A,正如预期的那样。但是当我尝试时:

int *B = NULL;
int *C = &*B;

结果是C == NULL,并且没有段错误。所以在获取地址之前&*B实际上并没有取消引用。B

我的猜测是,预处理器正在剥离实例,&*甚至*&在它们到达编译器之前,因为它们相互否定,但我找不到任何文档来验证这是标准C还是特定于编译器的。

预处理器是否剥离了&*and *&,我可以从任何给定的编译器中得到这种行为吗?

4

2 回答 2

31

这并没有被预处理器剥离,&*只是最终等同于指针本身,我们可以通过草拟 C99 标准 6.5.3.2 地址和间接运算符4段来看到这一点,其中说:

一元 * 运算符表示间接。如果操作数指向一个函数,则结果是一个函数指示符;如果它指向一个对象,则结果是一个指定该对象的左值。如果操作数的类型为 ''pointer to type'',则结果的类型为 ''type''。如果已为指针分配了无效值,则一元 * 运算符的行为未定义。87)

脚注 87 说:

因此,&*E 等价于 E(即使 E 是空指针),[...]

3段说(强调我的):

一元 & 运算符产生其操作数的地址。如果操作数的类型为“type”,则结果的类型为“pointer to type”。如果操作数是一元 * 运算符的结果,则该运算符和 & 运算符都不会被计算,结果就像两者都被省略了一样,除了对运算符的约束仍然适用并且结果不是左值。

更新

值得注意的是gcc,您可以使用标志(实时查看)和 Visual Studio /EP实时查看clang)查看预处理结果。-E

此外,值得注意的是,正如 MSalters 在他的评论中所说,仅拥有两个标记&*不足以理解上下文,如他的示例所示:

int *p, *q ;
int foo = *p & *q ;

&*因此,在预处理阶段甚至不可能仅删除,因为您没有足够的信息来确定&是运算符的地址还是按位和运算符的地址。

于 2014-01-21T01:20:43.743 回答
10

预处理器不会省略&*。但是,C 2011 标准在 6.5.3.2 3 中说&

如果操作数 [of & ] 是一元*运算符的结果,则该运算符和&运算符都不会被计算,结果就像两者都被省略了,除了对运算符的约束仍然适用并且结果不是左值。

根据上述约束的要求,*的操作数必须是指针类型。因此,&*33被本条款更改为;它违反了约束,因为3没有指针类型。此外,&*x没有完全更改为x,因为结果不是左值。例如,这是不允许的:

int a, *x;
x   = &a; // Normal, allowed.
&*x = &a; // Not allowed; `&*x` does not became exactly the same as `x`. 
于 2014-01-21T02:31:36.653 回答