为什么 ++*p++ 能够正常工作,而 ++i++ 却不行?

3
假设

p 是整数指针,i 是整数:

*p++ 给出与 p 相对应的 整数值

i++ 给出一个增加1的 整数值

由于行为上,以上两个表达式均返回整数,所以 ++*p++ ++i++ 不应该报告相同的错误。但是,为什么 ++*p++ 有效而 ++i++ 会导致编译器错误?

int main()
{
int a[10] = {0};

int *p = (int*)&a;
int i = 0;

// printf("%d", ++i++); -- FAILS error: lvalue required as increment operand   
printf("%d\n", ++*p++ ); // Prints 1
return 0;
}

编辑

++i++可以分解为以下内容:

i++
++(result)

这正是我困惑的地方: 同样地,我们可以将++*p++分解为
*p++ 
++(result).

*p++返回的是一个值(rvalue),而不是指针。那么为什么会有这种区别呢?


2
错误信息是什么?你能发一下展示这个问题的简短代码示例吗?此外,为什么你想要这样做呢?(最后一个只是好奇,但它确实给了一些C语言“只写不读”的声誉) - Levon
1
与你的问题无关,但是你的类型转换 int *p = (int*)&a; 是错误的。当编译器告诉你指针类型错误时,不要随意使用强制类型转换来消除警告/错误。相反,找出问题所在并进行修正。应该改为 int *p = a; - R.. GitHub STOP HELPING ICE
2个回答

17
后缀自增的结果是一个rvalue(右值),你不能修改它。++i++试图修改这个rvalue,但编译器会拒绝。

p++产生一个指针类型的rvalue,你不能修改它,但你可以对它进行解引用操作。 *p++解引用该rvalue,它会将其指向的值作为一个左值返回。然后前缀自增将修改它所指向的左值,而不是后缀自增产生的rvalue。

编辑:我可能还应该补充一点:即使编译器允许++i++,结果也是未定义的行为,因为它试图在没有中间序列点的情况下两次修改i。在++*p++的情况下,这种情况也不会发生——后缀自增会修改指针本身,而前缀自增会修改指针所指向的内容(在指针递增之前)。由于我们修改了两个完全不同的位置,因此结果不是未定义的行为。

如果你真的想这样做,仍然可以通过将指针初始化为指向自身来获得未定义的行为,在这种情况下,两个自增都会尝试修改指针。委员会没有很努力地防止这种情况,可能是因为只有真正追求完美的人才有可能想到这样一个疯狂的事情。

总之,在这种情况下,编译器大多数时间都在试图保护你不受伤害,但如果你足够努力,仍然能够弄坏代码。


0

++i++ 可以分解为以下内容:

i++
++(result)

问题: i++ 返回一个 rvalue,即一个“临时”值,不能进行递增操作。这是因为 i++ 在递增之前返回 i

这正是我困惑的地方 - 同样,我们可以将++p++分解为p++和++(result)。*p++返回一个值(rvalue),而不是指针。那么为什么会有差异呢? - Lunar Mushrooms
1
@Lunar *p++ 返回 *p 并在之后增加指向 p 的值。但是返回的值不是临时值,它存在于内存中,因此返回的值是一个左值而不是右值。这就是为什么您可以在其上调用 ++operator - Synxis
1
"(++i)++"不起作用。这是一个约束违规,原因完全相同。 - R.. GitHub STOP HELPING ICE
@R. 抱歉,我认为你是错的。证明:http://ideone.com/vWGVC(`++i`返回一个右值) - Synxis
@Synxis:没有正确阅读问题。你提供的链接是:*语言:C++ (gcc-4.3.4)*,但是问题的标签是c - R.. GitHub STOP HELPING ICE
@R. 哦,我明白了。问题被重新标记了,因为我在“最新的C ++”中找到了它... :( 已修复。 - Synxis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接