前缀(++x)和后缀(++ +)操作是如何工作的
How do Prefix (++x) and Postfix (x++) operations work?
有人能告诉我前缀/后缀操作符是如何工作的吗?我一直在网上找,但没有找到任何东西。
据我所知,前缀首先加1,然后做运算,然后赋值。
后缀将先执行操作,然后赋值,然后自增。
但是我的代码有点问题:
int x, y;
x = 1;
y = x + x++; // (After operation y = 2)(x=2)
但是当我这样做的时候:
y = x++ + x; // (After operation y = 3)(x=2)
我不知道为什么这些操作会有什么不同。我有两个问题:
你能解释一下这两者的区别吗?
这如何适用于其他操作符前缀?
这个问题经常被问到。请注意,每次有人问这个问题,都会有很多人给出错误的答案。很多人对这些运算符的工作方式有不正确的认识,包括那些写编程书的人,他们教别人一些错误的东西。仔细阅读这里的其他答案。
关于c#行为的正确分析,请参见:i++和++i的区别是什么?
对于c++,任何行为都是正确的行为,在你观察到副作用的情况下。 c++没有定义什么时候递增的副作用是可见的。任何两个编译器都可以做不同的事情。
在任何语言中,都不要依赖于副作用发生的顺序,但是在c++中,当然也不要依赖于,因为它不可靠。看你的具体情况:
int x, y;
x = 1;
y = x + x++;
你报告x和y都是2。这在c#中是正确的。在c#中正确的行为是:
- 将y作为变量求值
- 计算x作为一个值——它是1
- 将x++作为值计算。它将x作为一个变量计算,然后取其原始值1,然后将该值加1,即2,然后将2赋值给x,然后得到原始值1。
- 计算1 + 1,即2
- 将2赋值给y
所以x和y在c#中都是2
c++也可以做同样的事情,但是允许按从右到左的顺序计算加法。也就是说,允许这样做:
- 将x++作为值计算。它将x作为一个变量计算,然后取其原始值1,然后将该值加1,即2,然后将2赋值给x,然后得到原始值1。
- 将x作为值计算——它是2
- 计算1 + 2,即3
- 将y作为变量求值
- 将3赋值给y
c++也允许这样做:
- 将x++作为值计算。它将x作为一个变量求值,然后取其初始值1,然后将该值加1,即2…这里缺了一步……然后得到原始值,即1。
- 计算x作为一个值——它是1
- 计算1 + 1,即2
- 将2赋值给x——之前缺失的步骤。
- 将y作为变量求值
- 将2赋值给y
所以在c++中,你可以得到y为3或2,这取决于编译器编写者的突发奇想。c#中y总是等于2。在c++中,增量的赋值可以在的任何时间发生,只要它确实发生。在c#中,递增的赋值必须发生在计算递增的值之后,在使用原始值之前。(当从执行线程观察时;如果你试图从另一个或多个线程中观察这些东西,所有的赌注都是无效的。
在第二个例子中:
y = x++ + x;
在c#中需要的行为是:
- 将y作为变量求值
- 将x++作为值计算。它将x作为一个变量计算,然后取其原始值1,然后将该值加1,即2,然后将2赋值给x,然后得到原始值1。
- 将x作为值计算——它是2
- 计算1 + 2,即3
- 将3赋值给y
所以c#的正确答案是y = 3 x = 2
同样,c++可以按任何顺序执行这些步骤。c++允许做:
- 计算x作为一个值——它是1
- 将x++作为值计算。它将x作为一个变量计算,然后取其原始值1,然后将该值加1,即2,然后将2赋值给x,然后得到原始值1。
- 计算1 + 1,即2
- 将y作为变量求值
- 将2赋值给y
同样,在c++中,正确的答案是y是2或3,这取决于编译器编写器的心血来潮。在c#中,正确的答案是y = 3
- c#中
+
的操作数是按从左到右的顺序求值的。 - 在C和c++中,
+
的操作数的求值顺序没有指定。
对于c#,您的示例如下:
y = x + x++;
^ x is 1
^ x is increased to 2, but the postfix increment returns the old value (1)
y = 2
y = x++ + x;
^ x becomes 2, but postfix increment returns the old value (1)
^ x is now 2 here
y = 3
在C和c++:
输出为Unspecified。
参考- c++ 03标准:
第五节:表达式,第4段:
除非特别注明[例如:&&和||],单个操作符和单个表达式的子表达式的操作数的求值顺序以及副作用发生的顺序未指定。
在C99第6.5节。
"操作符和操作数的分组由语法指示。72)除后面指定(对于function-call()、&&、||、?:和逗号操作符)外,子表达式的求值顺序和副作用发生的顺序都未指定。"
在这两种情况下,增量都是在使用x之后应用的。在第一种情况下,评估如下:Y = 1 + 1(自增为2)
在第二个
y = 1(自增到2)+ 2.
这就是为什么你得到不同的答案。
表达式x++
和++x
既具有结果(或值),又具有副作用。
如果将讨论限制为整型操作数,则x++
的结果等于x
的当前值。的副作用是使x
增加1。因此,给定代码
x = 0;
y = x++;
结果将是x
== 1和y
== 0(假设x
和y
是整型)。
对于++x
, 结果等于1加上x
的当前值。的副作用是将x
增加1。因此,给定代码
x = 0;
y = ++x;
结果将是x
== y
== 1。
C和c++与c#的区别在于计算操作数的时间和产生副作用的时间。c#保证表达式中的操作数总是从左到右求值。C和c++只保证从左到右计算&&
、||
、?:
、逗号和函数调用()
操作符——对于所有其他操作符,计算操作数的顺序是未指定。
x++
和++x
的副作用将在表达式求值之后立即应用,而C和c++只要求在下一个序列点之前应用副作用。c#的求值规则保证像x = x++
、a = b++ * b++
和a[i] = i++
这样的表达式是定义良好的,而C和c++语言的定义明确地说这样的表达式会导致未定义的行为(任何结果都是可能的)。
x + x++和x++ + x是您不想依赖的病理副作用的例子。x++和++ X都增加X,但在增加X时,计算顺序是未定义的-编译器可以选择先计算哪一边。
考虑:
y = x + x++;
它的行为是否被定义(它在C和c++中未定义;显然c#中定义得很好),不管你想做什么,肯定有更好的表达方式。
如果你假设严格的从左到右求值,那么上面的可以写成:
y = x * 2;
x ++;
对于任何知道=
、*
和++
是什么意思的读者来说,其含义都是清晰而明确的,并且未来的代码维护者也不会试图找到您。
如果你不相信编译器能生成高效的代码,你也可以写x + x
或x << 1
,但这种不信任通常是错误的。
如果你坚持的话,你甚至可以这样写:
y = x++ * 2;
这对我个人的口味来说有点简洁,但它仍然是明确的。
如果你想理解别人的代码(不可否认程序员要花很多时间去做这件事),那么理解复杂的表达式是很重要的。但是,当您编写自己的代码时,清晰度比节省击键次数(或展示您对操作符优先级图的了解程度)更重要。
- QSqlquery prepare()和bindvalue()不工作
- 导入库可以跨dll版本工作吗
- 以螺旋方式打印矩阵的程序.(工作不好)
- 对象指针在c++中是如何工作的
- 为什么在Windows上的VS 2019和Clang 9中"size_t"在没有标题的情况下工作
- VSOMEIP-2个设备之间的通信(TCP/UDP)不工作
- 为字符串中每 N 个字符插入空格的函数没有按照我认为的方式工作?
- C++为线程工作动态地分割例程
- 为什么我的 std::ref 无法按预期工作?
- 布尔比较运算符是如何在C++中工作的
- SampleConsensusPrerejective(ext.RANSAC)是如何真正工作的
- 不确定要在我的main中放入什么才能使我的代码正常工作
- 为什么std::condition_variable notify_all的工作速度比notify_one快(对于随机请
- <<操作员在下面的行中工作
- 有人能解释一下为什么下界是这样工作的吗C++的
- ExtractIconEx:可以工作,但偶尔会崩溃
- C++中的memset函数工作不正常
- 当我在第一个循环中使用"auto"时,它工作正常,但是使用"int"它会给出错误,为什么?
- 链表c++插入,所有情况都已检查,但没有任何工作
- 当 int 方法工作正常时,void 方法有何不同,或者为什么我不能调用 void 方法?