后增量和前增量的概念?


Answers:


118

到目前为止,所有四个答案都是不正确的,因为它们断言了事件的特定顺序。

相信“城市传奇”已导致许多新手(和专业人士)误入歧途,涉及表达式中未定义行为的问题不断。

所以。

对于内置的C ++前缀运算符,

++x

递增x并产生(作为表达式的结果)x作为左值,而

x++

递增x并产生(作为表达式的结果)的原始值x

特别x++是因为没有时间顺序暗示的原始值的增加和产生x。编译器可以自由发出机器代码,该机器代码生成的原始值x,例如可能存在于某个寄存器中,并且将增量延迟到表达式的末尾(下一个序列点)。

人们错误地认为增量一定是第一位的,而且他们很多,他们常常得出这样的结论:某些表达式实际上具有未定义的行为时,必须具有定义明确的效果。


7
@ Sa'me Smd:“ lvalue”本质上是对具有存储对象的引用。它是源自早期C语言的术语,在C语言中,赋值需要在左侧有一个左值。
干杯和健康。-Alf 2010年

3
您对机制的看法是正确的,但我认为您在别人看来是错误的,在概念上是错误的。机制只是尝试实施该概念的实用性。“发布”表示之后-从概念上讲,增量是在之后进行的。整个“顺序”概念(如“顺序点”)都意味着一个顺序,而UB则是由于误解了顺序点是如何工作的,而不是“误解”了“前置”和“后置”的含义。
sje397 2010年

2
那么表达式会(i++ > 0) && someProperty(myArray[i])做什么呢?就像i = 5一样,它将someProperty使用myArray[5]还是调用myArray[6]
AJMansfield

4
@AJMansfield:在C ++ 03术语中,内置函数&&引入了一个序列点(C ++ 03§1.9/ 18)。在C ++ 11术语中,内置&&函数的左手操作数表达式在右手操作数表达式之前排序(C ++ 11§5.14/ 2)。这意味着,如果在调用时,它与由i值6
欢呼和心连心。-Alf 2012年

3
@AJMansfield:&&||在提供短路评估(以及序列点)方面是独特的。三元选择运算符有点像这样,因为它保证不对未采取的选择进行评估。但是对于算术运算符,当您在​​同一表达式中修改并使用变量时,您只会得到未定义行为。我认为但不确定,所有其他运营商也是如此。无论如何,这是最好的假设,并且编写依赖于某些微妙的特殊情况(很少有程序员知道)的代码是不好的。;-)
干杯和hth。-阿尔夫

28
int i, x;

i = 2;
x = ++i;
// now i = 3, x = 3

i = 2;
x = i++; 
// now i = 3, x = 2

“发布”表示之后-即,在读取变量后完成增量。“ Pre”表示之前-因此变量值先增加,然后在表达式中使用。


“在读取变量后完成增量”。我曾经以为我理解后缀和前缀增量,但是现在您让感到困惑!
wilhelmtell

为什么这样令人困惑?我们正在谈论“前”和“后”增量的概念。这些前缀确实意味着之前之后
sje397 2010年

5
“ preincrement”和“ postincrement”中的“ pre”和“ post”是指运算符相对于操作数的位置。它们并不意味着相对于何时读取变量,何时发生增量之间的任何时间顺序。
James McNellis 2010年

2
@James:我理解你的意思-但是为了说明,想象一下如果操作符的功能被反转会造成多大的混乱。我知道,从技术上讲,没有时间顺序的含义,但肯定是有后增量的想法的“递增的概念映射使用”。
sje397 2010年

@ sje397为什么例如int a = 5; cout << a ++; 根据您的示例仅打印5个而不是6个?
RaHuL

17

后缀增量x++前缀增量之间的区别++x恰好在于两个运算符如何评估其操作数。后缀增量在概念上将操作数复制到内存中,对原始操作数进行增量,最后产生副本的值。我认为最好通过在代码中实现运算符来说明这一点:

int operator ++ (int& n)  // postfix increment
{
    int tmp = n;
    n = n + 1;
    return tmp;
}

上面的代码无法编译,因为您无法为原始类型重新定义运算符。编译器在这里也不能告诉我们定义的是后缀运算符,而不是prefix,但让我们假设这是正确且有效的C ++。您可以看到后缀运算符确实作用于其操作数,但是它在递增之前返回了旧值,因此表达式的结果x++是递增之前的值。x但是,递增的。

前缀增量也会递增其操作数,但在递增会产生操作数的值:

int& operator ++ (int& n)
{
    n = n + 1;
    return n;
}

这意味着该表达式的++x计算结果为x after增量的。

容易想到该表达式++x因此等同于assignmnet (x=x+1)。但是,这并非完全如此,因为增量是一种在不同上下文中可能表示不同事物的操作。在简单的原始整数的情况下,确实++x可以用代替(x=x+1)。但是,在类类型的情况下(例如链表的迭代器),迭代器的前缀增量绝对绝对不意味着“向对象添加一个”。


16

没有人回答这个问题: 为什么这个概念令人困惑?

作为本科计算机科学专业的学生,​​由于我的阅读方式,我花了一些时间来理解它代码。

以下不正确!


x = y ++

X等于y增量。从逻辑上看,这意味着增量操作完成,X等于Y的值。在之后发表意思。

要么

x = ++ y
X等于y递增。从逻辑上看,这意味着X等于增量操作完成之前的Y值。意思之前


它的工作方式实际上是相反的。这个概念令人困惑,因为该语言具有误导性。在这种情况下,我们不能使用单词来定义行为。
x = ++ y实际上是读取的,因为增量之后X等于Y的值。
实际上会读取x = y ++,因为X等于增量之前的Y值。

就英语的语义而言,“前”和“后”词是后退的。它们仅表示++在Y关系中的位置。仅此而已。

就个人而言,如果可以选择的话,我会切换++ y和y ++的含义。这只是我必须学习的一个成语的例子。

如果有办法解决这种疯狂问题,我想简单地说一下。

谢谢阅读。


15
“如果有一种应对这种疯狂的方法,我想简单地说一下。” -我认为这是这样:PRE增量(y = ++ x):先递增x,然后再分配给y。对于POST增量(y = x ++):分配给y,然后增量x。因此,将pre&post视为“ x何时递增”而不是“ x y可获得什么版本”。那是我的“疯狂方法”,这是完全
合理的

“如果有一种应对这种疯狂的方法,我想简单地说一下。” 好吧,当然有。增的意思是“操作来预先又名之前的操作,因此增量来之前的操作数返回给调用者,使他们获得的价值包括的增量。” 增量的意思是“操作员来又名的操作,因此增量来(复印件)操作数返回给调用者,所以他们得到的值不包括的增量。”
underscore_d

++y是pre递增,因为++用作前缀;y++是post递增,因为++用作后缀(或“后缀”)。完全不违反英语。
法拉普

“相对于英语的语义,前置和后置单词是后退的。” 我不同意。“前置”和“后置”修饰“增量”,并准确地描述了增量在概念上何时发生。从概念上讲,“预递增”产生值之前递增。从概念上讲,“后递增”产生值递增。因此,使用预增量,您将获得增量值。使用后增量,您将获得原始价值。
丹尼尔·扬科斯基

6

很简单 两者都会增加变量的值。以下两行相等:

x++;
++x;

区别在于是否使用递增的变量的值:

x = y++;
x = ++y;

在此,两行都将y的值加1。但是,第一个将增量之前的y值分配给x,第二个将增量之后的y值分配给x。

因此,当增量也用作表达式时,只有一个区别。返回值后,后增量递增。预增量在之前递增。


4
两条线根本不相等。-1
wilhelmtell

4
wilhemtell:谢谢您对讨论完全没有添加任何内容。做得好。
乔纳森·伍德

1
@JonathanWood是什么类型x?你有它。两条线不相等。我现在可以在讨论中添加任何内容吗?
wilhelmtell

1
@JamesMcNellis,除非x是类类型的period,否则前两个语句具有相同的效果。
wilhelmtell

4
@wilhelmtell:如果重载是一致的(应该是重载),则语句和的效果是相同的。是的,您调用了不同的函数,但是它们应该执行相同的操作。那就是我想要达到的目的。x++;++x;
James McNellis 2010年

4
int i = 1;
int j = 1;

int k = i++; // post increment
int l = ++j; // pre increment

std::cout << k; // prints 1
std::cout << l; // prints 2

后增量表示将值i赋给后增加k。但是,预增量表示将值j赋值给它之前先增加l

递减也是如此。


竖起大拇指举例说明
Rajesh,2015年

1

由于我们现在有内联JavaScript代码段,因此我不妨添加一个交互式的pre和pos增量示例。它不是C ++,但概念保持不变。

let A = 1;
let B = 1;

console.log('A++ === 2', A++ === 2);
console.log('++B === 2', ++B === 2);


1

后增量:

int x, y, z;

x = 1;
y = x++; //this means: y is assigned the x value first, then increase the value of x by 1. Thus y is 1;
z = x; //the value of x in this line and the rest is 2 because it was increased by 1 in the above line. Thus z is 2.

预增:

int x, y, z;

x = 1;
y = ++x; //this means: increase the value of x by 1 first, then assign the value of x to y. The value of x in this line and the rest is 2. Thus y is 2.
z = x; //the value of x in this line is 2 as stated above. Thus z is 2.

0

发布增量(a ++)

如果int b = a ++,则表示

int b = a;

a = a+1;

在这里,我们将值加1。在增加之前返回该值,

例如a = 1; b = a ++;

然后b = 1和a = 2

预增量(++ a)

如果int b = ++ a; 那么这意味着

a=a+1;

int b=a ;

预先增加:这会将1加至主要值。进行增量后,将返回该值,如果a = 1;b = ++ a; 然后b = 2和a = 2。


-1

从C99标准开始(C ++应该相同,除非出现奇怪的重载)

6.5.2.4 Postfix增减运算符

约束条件

1后缀递增或递减运算符的操作数应具有合格或不合格的实数或指针类型,并且应为可修改的左值。

语义学

2后缀++运算符的结果是操作数的值。获得结果后,操作数的值将递增。(即,将适当类型的值1添加到其中。)有关约束,类型和转换以及操作对指针的影响的信息,请参见加法运算符和复合赋值的讨论。更新操作数存储值的副作用将发生在上一个和下一个序列点之间。

3 postfix-运算符类似于postfix ++运算符,只是操作数的值减小(即从中减去适当类型的值1)。

6.5.3.1前缀递增和递减运算符

约束条件

1前缀递增或递减运算符的操作数应具有合格或不合格的实数或指针类型,并且应为可修改的左值。

语义学

2前缀++运算符的操作数的值增加。结果是递增后操作数的新值。表达式++ E等效于(E + = 1)。有关约束,类型,副作用和转换以及操作对指针的影响的信息,请参见加法运算符和复合赋值的讨论。

3前缀-运算符类似于前缀++运算符,只是操作数的值递减。


9
从ISO标准中发布大量文本而没有任何评论或解释并没有真正的帮助,尤其是当文本不完全独立时(从该文本来看,什么是序列点?什么是左值?该左值怎么能是合格还是不合格?“加法运算符和化合物赋值的讨论”在哪里?同样,假定C ++相同通常是一个坏主意。两种语言之间有许多细微但重要的差异,即使在操作员行为之类的简单事物中也是如此。
James McNellis 2010年

2
抱歉,用另一种语言发布标准的一部分并没有特别帮助。大多数情况下,c ++中的增量运算符用于类类型,这使此操作更加混乱而不是有用。
daramarak

-4

预增量在增量值之前,++例如:

(++v) or 1 + v

后期递增是在值递增之后,++例如:

(rmv++) or rmv + 1

程序:

int rmv = 10, vivek = 10;
cout << "rmv++ = " << rmv++ << endl; // the value is 10
cout << "++vivek = " << ++vivek; // the value is 11

第一个增量是前增量,第二个增量是后增量
RMVIVEK ARNI

-7

您还应该知道,C / C ++和Java中后递增/递减运算符的行为是不同的。

给定

  int a=1;

在C / C ++中的表达式

 a++ + a++ + a++

计算结果为3,而在Java中计算结果为6。

这个例子更加令人困惑:

cout << a++ + a++ + a++ << "<->" << a++ + a++ ;

打印9 <-> 2 !! 这是因为上面的表达式等效于:

operator<<( 
  operator<<( 
    operator<<( cout, a++ + a++ ), 
    "<->"
  ), 
  a++ + a++ + a++ 
)

1
这可能是由于C99标准所说的:“更新操作数存储值的副作用将发生在上一个和下一个序列点之间。”
Pedro J. Ponce deLeón13年

我用C和C ++编译器尝试过。评估结果为6。使用什么编译器得到3?
Foo Bar

5
在一个表达式中两次使用前/后增量运算符时,这是未定义的行为
Galik 2016年

2
语句“在C / C ++中表达式a ++ + a ++ + a ++计算为3 ”简直是错误的。正如@Galik指出的那样,它导致未定义的行为。
快乐的绿色孩子午睡
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.