后缀(前缀)增量、左值和右值(在 C 和 C++ 中)

Posted

技术标签:

【中文标题】后缀(前缀)增量、左值和右值(在 C 和 C++ 中)【英文标题】:postfix (prefix) increment, L-value and R-value (in C and C++) 【发布时间】:2014-02-16 13:49:43 【问题描述】:

我刚刚了解到以下事实:

前缀递增 (++var_name) 的结果是 C 中的 R 值(至少,我是 确保它不是 C 中的 L 值),但它是 C++ 中的 L 值。

后缀递增 (var_name++) 的结果是 C 中的 R 值(至少,我是 确保它不是 C 中的 L 值)。这在 C++ 中也是如此(它表示结果 是prvalue)。

我在 VS2010(.cpp 和 .c)和 Ubuntu(gcc 和 g++)中检查了这些。

在 C++ Standard http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf 的 p.109 (5.3.2) 中这样写

前缀 ++ 的操作数通过加 1 进行修改,如果为 bool 则设置为 true(不推荐使用此用法)。这 操作数应为可修改的左值。操作数的类型应为算术类型或指向 完全定义的对象类型。 结果是更新后的操作数;它是一个左值,并且...

在第 101 页,(5.2.6)

后缀 ++ 表达式的值是其操作数的值。 ... 结果是 prvalue。结果的类型是操作数类型的 cv 非限定版本。另见 5.7 和 5.17。

(虽然我不知道 R-value 和 prvalue 之间的区别)。

关于C标准http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf, 前缀++在6.5.3.1中描述,后缀在6.5.2.4中,但从描述中,我无法得到明确、确定的答案。

我想知道使它们成为 R 值或 L 值的原因。我只知道

We can assign a value to a (modifiable) L-value, for example, a variable name. R-value is a value of an expression.

但我不知道为什么 postfix ++ 在 C 和 C++ 中不是 L 值,以及为什么 prefix ++ 在 C 中没有。(我看到类似“postfix ++...存储...在一个临时地址,然后...”,但我还是不明白)。

还有一个问题是为什么前缀++在C和C++中是不同的?使前缀 ++ 成为 L 值(在 C++ 中)有很多优点?如果是这样,为什么C不改变这个? (除了向后兼容性之外的其他原因,或者至少为什么更改它会导致很多问题)。

【问题讨论】:

您说您在 VS2010 和 gcc 中检查了您的假设。您使用什么代码来验证您对这些问题的假设? ""(我看到类似“postfix ++...store...in a temporary address, then...”,但我还是不明白)"" - 也许如果您引用了您没有得到的标准中的整个句子,有人可以为您澄清。 @Brandin,它不在标准中。我在一本书上看到它,上面写着“第三条语句中的表达式 a++ 是一个右值,因为它临时存储 a 的值作为表达式的结果,然后递增 a 。” 另外,只需使用 (a++) = 3(++a) = 3 之类的语句。 这些示例在不同的 C 和 C++ 编译器上会产生不同的结果吗?我以为你的问题是关于 C 和 C++ 之间的区别。 【参考方案1】:

C 和 C++ 是不同的语言。 C++ 有运算符重载,而 C 没有。 ++ 运算符,无论是前缀还是后缀,都是可以在 C++ 中重载的运算符。 C++ 也有引用,而 C 没有。

在 C 中,++ii++ 都产生一个不是 左值 的值。这是可取的,否则您可能会通过尝试在相同的序列点边界内修改相同的标量而与未定义的行为发生冲突。

深思:在 C 中,逗号运算符也产生一个不是左值的值,所以要“删除”左值性,你可以这样做:

(0, lvalue)

【讨论】:

【参考方案2】:

这是真的

pre 递增/递减运算符(++var 或 --var)产生一个左值(即一个可修改的对象)

后递增/递减运算符(var++ 或 var--)产生一个右值(即临时对象)。

考虑以下带有预递增/递减运算符的代码



int i = 0;

int* pi = &(++i);


没关系,因为实际上它的伪代码是

i = i+1; // pre increment i

int* pi = &i; // then evaluate its address and assign it to pi 

现在考虑相同的代码,但使用后递增/递减运算符以及编译器接受此无效代码的后果



int i = 0;

int* pi = &(i++); // Not OK !! because it is a temporary variable


它的伪代码是

int i = 0;

int tmp = i; // compiler creates a temporary variable to save value of i

int* pi = &tmp; // then would take the address of a temporary variable 

i = i + 1; 

【讨论】:

以上是关于后缀(前缀)增量、左值和右值(在 C 和 C++ 中)的主要内容,如果未能解决你的问题,请参考以下文章

C++:神一样的左值

具有适用于左值和右值的引用参数的 C++ 函数

C++ 函数透明地采用左值和右值参数

C++内功修炼干货,进大厂必须会的C++左值与右值,最适合小白看的文章!

左值和右值上的 C++ std::is_integral 显示不同的结果

左值和右值