关于++运算符的C和C ++的区别
我一直在玩一些代码,看到一些我不明白的“为什么”。
int i = 6; int j; int *ptr = &i; int *ptr1 = &j j = i++; //now j == 6 and i == 7. Straightforward.
如果把操作员放在等号的左边呢?
++ptr = ptr1;
相当于
(ptr = ptr + 1) = ptr1;
而
ptr++ = ptr1;
相当于
ptr = ptr + 1 = ptr1;
后缀运行编译错误,我明白了。 你在赋值运算符的左边有一个常量“ptr + 1”。 很公平。
前缀之一编译和工作在C + +。 是的,我明白这是混乱的,你正在处理未分配的内存,但它的工作和编译。 在C这不编译,返回相同的错误作为后缀“作为赋值左操作数所需的左值”。 无论如何编写,使用两个“=”运算符或“++ ptr”语法进行扩展。
C如何处理这样一个赋值和C ++如何处理它有什么区别?
在C和C ++中, x++
的结果是一个右值,所以你不能指定它。
在C中, ++x
相当于x += 1
(C标准§6.5.3.1/ p2;所有的C标准都是WG14 N1570)。 在C ++中, ++x
如果x
不是bool
(C ++标准§5.3.2[expr.pre.incr] / p1;所有C ++标准列表都是WG21 N3936),则++x
等价于x += 1
。
在C中,赋值expression式的结果是一个右值(C标准§6.5.16/ p3):
赋值运算符将值存储在由左操作数指定的对象中。 赋值expression式在赋值后具有左操作数的值,但不是左值。
因为它不是左值,所以你不能指定它:(C标准§6.5.16/ p2 – 注意这是一个约束)
赋值运算符的左操作数应该有一个可修改的左值。
在C ++中,赋值expression式的结果是一个左值(C ++标准§5.17[expr.ass] / p1):
赋值运算符(=)和复合赋值运算符全部从右到左。 所有需要一个可修改的左值作为它们的左操作数,并返回一个左值指向左操作数。
所以++ptr = ptr1;
在C中是可诊断的约束违规,但不违反C ++中的任何可诊断规则。
但是,pre-C ++ 11, ++ptr = ptr1;
具有未定义的行为,因为它在两个相邻序列点之间修改ptr
两次。
在C ++ 11中, ++ptr = ptr1
的行为变得很好定义。 如果我们把它改写成更清楚
(ptr += 1) = ptr1;
由于C ++ 11,C ++标准规定(§5.17[expr.ass] / p1)
在任何情况下,赋值都是在左右操作数的值计算之后,赋值expression式的值计算之前进行sorting的。 对于一个不确定sorting的函数调用,复合赋值的操作是一个单独的评估。
所以在ptr += 1
ptr1
和ptr1
的值计算之后,由=
执行的赋值被sorting。 由+=
执行的赋值在ptr += 1
的值计算之前被sorting,并且+=
所需的所有值计算必须在该赋值之前被sorting。 因此,这里的sorting是明确的,没有不确定的行为。
在C中,前后增量的结果是右值 ,我们不能赋值右值 ,我们需要左值 ( 另请参阅:在C和C ++中理解 左值和右值 )。 我们可以看看C11标准草案 6.5.2.4
后缀增减操作符 ( 强调我的前进 ):
后缀++运算符的结果 是操作数的值 。 […]查看添加剂操作符和复合赋值的讨论,了解有关约束,types和转换的信息以及操作对指针的影响。 […]
所以后增量的结果是一个与右值同义的值 ,我们可以通过参考6.5.16
节的赋值运算符来进一步了解约束和结果,从而证实了这一点。
[…]赋值expression式在赋值之后有左操作数的值, 但不是左值 。[…]
这进一步证实了后增加的结果不是左值 。
对于预增量,我们可以从6.5.3.1
节的前缀增量和减量运算符中看到:
[…]请参阅加法运算符和复合赋值的讨论,以获取有关约束,types,副作用和转换的信息以及操作对指针的影响。
也像后期增量一样指向6.5.16
,因此C中的预增量结果也不是左值 。
在C ++中,后期增量也是一个右值 ,更具体地说是一个值 ,我们可以通过参考5.2.6
节的增量和减量来证实这一点:
[…] 结果是一个prvalue。 结果的types是操作数types的cv不合格版本[…]
关于预增C和C ++有所不同。 在C中,结果是一个右值,而在C ++中,结果是一个左值 ,它解释了为什么++ptr = ptr1;
在C ++中工作,但不是C.
对于C ++,这在5.3.2
中有介绍:
[…]结果是更新的操作数; 它是一个左值 ,如果操作数是一个位域,则它是一个位域[…]。
要了解是否:
++ptr = ptr1;
定义良好或不在C ++中,我们需要两个不同的方法,一个用于C ++ 11之前,另一个用于C ++ 11。
Pre C ++ 11此expression式调用未定义的行为 ,因为它在相同的序列点内不止一次地修改对象。 我们可以通过Pre C ++ 11草案标准第5
节expression式来看到这一点:
除非另有说明,否则未指定单个操作符的操作数和个别expression式的次expression式的顺序以及副作用发生的顺序.57) 在前一个顺序点和下一个顺序点之间,标量对象应该修改其存储值最多一次由一个expression式的评估。 此外,只有在确定要存储的值时才能访问先前值。 对于一个完整expression式的子expression式的每个可允许的sorting,应满足本段的要求; 否则行为是不确定的。 [例如:
i = v[i ++]; / / the behavior is undefined i = 7 , i++ , i ++; / / i becomes 9 i = ++ i + 1; / / the behavior is undefined i = i + 1; / / the value of i is incremented
– 例子]
我们递增ptr
,然后分配给它,这是两个修改,在这种情况下,序列点出现在expression式的后面;
。
对于C + 11,我们应该去缺陷报告637:sorting规则和示例不一致 ,这是导致的缺陷报告:
i = ++i + 1;
在C ++ 11中成为明确的行为,而在C ++ 11之前,这是未定义的行为 。 这个报告中的解释是我见过的最好的一个,多次阅读都很有启发性,帮助我从新的angular度理解了许多概念。
导致这个expression式变得定义良好的行为的逻辑如下:
-
分配副作用需要在LHS和RHS(5.17 [expr.ass]段落1)的值计算之后进行sorting。
-
LHS(i)是一个左值,所以它的值计算包括计算i的地址。
-
为了计算RHS(++ i + 1),有必要首先对左值expression式++ i进行值计算,然后对结果进行左值到右值的转换。 这保证了增量副作用在计算加法运算之前被sorting,而运算又是在分配副作用之前sorting的。 换句话说,它为这个expression式产生一个明确定义的顺序和最终值。
逻辑有点类似于:
++ptr = ptr1;
-
LHS和RHS的值计算在分配副作用之前被sorting。
-
RHS是一个左值,所以它的值计算包括计算ptr1的地址。
-
为了计算LHS(++ ptr),有必要先计算左值expression式++ ptr,然后对结果进行左值到右值的转换。 这保证了增量副作用在分配副作用之前被sorting。 换句话说,它为这个expression式产生一个明确定义的顺序和最终值。
注意
OP说:
是的,我明白这是混乱的,你正在处理未分配的内存,但它的工作和编译。
指向非数组对象的指针被认为是用于加法运算符的大小为1的数组,我将引用C ++标准草案,但是C11几乎具有完全相同的文本。 从5.7
节添加剂操作员 :
对于这些运算符来说,指向非数组对象的指针的行为与指向长度为1的数组的第一个元素的指针相同,该对象的types为元素types。
并进一步告诉我们指向一个过去的数组的末尾是有效的,只要你不取消引用指针:
[…]如果指针操作数和结果指向同一个数组对象的元素, 或者一个超过了数组对象的最后一个元素 ,则评估不会产生溢出; 否则,行为是不确定的。
所以:
++ptr ;
仍然是一个有效的指针。
- 我怎样才能并行运行NUnittesting?
- 为什么MSBuild在C:\ for Microsoft.Cpp.Default.props而不是c:\ Program Files(x86)\ MSBuild中查找? (错误MSB4019)