采用以下代码(可用作控制台应用程序):
1 2 3 4 5 6 7
| static void Main(string[] args)
{
int i = 0;
i += i++;
Console.WriteLine(i);
Console.ReadLine();
} |
i的结果为0。我期望2(就像我的一些同事那样)。可能编译器创建了某种结构,导致i为零。
我期望2的原因是,在我的思路中,右边的语句将首先被评估,用1递增i。因为我已经是1,所以它是1对1。所以1+1=2。很明显,这不是正在发生的事情。
你能解释编译器是做什么的吗,或者在运行时会发生什么?为什么结果为零?
某种免责声明:我绝对知道您不会(也可能不应该)使用此代码。我知道我永远不会。然而,我发现很有趣的是,我知道它为什么会这样做,以及到底发生了什么。
- 预期结果不应该是1吗?I(0)+=I++(1),因此0+=1=1
- @aleaton使用postfix得到0,as+=只计算一次i,postfix最后递增。要得到1,必须使用前缀。
- 它按预期工作
- 这个问题有多少变种?
- 预增量将在进行计算之前增加值i+=++i将给您1
- 编译器所做的是正确的。这也是一种比赛条件!
- 你怎么大声念出"EDOCX1"〔1〕?我建议你总是把它读作"我在你的头脑里增加",而不是"我加上"。:)
- 我认为这是每个人在学校学到的关于学前和学后增量的第一件事…那些人…;)
- 为什么每个人都把注意力集中在前增量和后增量上?奇怪的是,在评估右侧之前,+=左侧的i的值被"缓存"。这是违反直觉的,例如,如果i是一个对象,就需要一个复制操作。(请不要误解我的意思:我完全同意声明,0是正确和标准的一致答案。)
- @Walkereno这种困惑正是为什么我甚至不认为在C中需要++或--。似乎您总是在不使用这些运算符的情况下更清楚地编写代码。
- @我认为他们期望它先增加,然后再增加,结果是2。
- -1:不缺岗位、场所、文章、书籍章节和凿字板,说明岗位和增量前操作员,并展示其固有的混淆性质。不幸的是,坚持以这种方式使用它们的开发人员甚至更少。:(
- 这并不能回答你的问题,但是无论i += i++;打算做什么,我保证有一种更简单、更清晰的方法来做。
- 我用函数分析了这个问题,发现非常清楚:stackoverflow.com/a/13535004/195417…你同意吗?还是我还缺什么?
- 拜托,这是你们在"你好世界"之后应该知道的基本知识。
- @实际上,在c中,这个表达式产生了1(gcc),正如我们许多人所期望的,这就是使问题有趣的原因。
- -我见过这个问题被问了5次,我在这里的时间不超过3年。
- 我刚刚用C++测试了它(它产生EDCOX1,9)。从今以后,我将用它作为无可争辩的证据,证明C++是高级语言。P
- 外星人
- 为什么,哦,你们为什么用这种耻辱来编码风格?绝不,绝不,绝不,绝不在左右手相同变量上使用后或前增量或减量。这个表达式令人困惑,因为它不能被使用。曾经。从未。
- 这个问题引起了如此多的讨论,正是这个事实证明了这个结果是出乎意料的。因此,为了维护和可读性的目的,代码的意图是非常明确的,因此是非常糟糕的实践。所以对于任何读到这篇文章的人:不要使用这样的快捷方式。只需编写清晰而简单的代码,这样每个人都能清楚地知道您正试图完成什么。
- 为什么每个人都默认使用post inc/dec操作符而不是pre操作符?后置操作效率较低,并且会产生令人惊讶的副作用——除非你确定这是你想要的,否则不应该使用它们。例如,"for"循环应该使用"++i",而不是"i++"—但是大多数程序员似乎出于某种原因使用了第二个循环。我认为这个错误是很久以前因为某种原因被抓住的,它会被所有人复制并默认使用。
- @Ken Beckett,但是这需要你考虑到你在for循环中的i将从1开始(你可以设置i = -1或在循环逻辑中使用i-1,但我认为大多数人会发现这看起来很奇怪)。所以我们使用i++,我相信误解来自于这种用法。
- 我不敢相信这个问题有88票赞成…后缀递增/递减和运算符优先级是编程的基础知识。
- @彼得,不,这是错误的-在"for"循环的迭代(第三个)表达式中使用"++i"或"i++"会产生相同的行为。即使使用底部带有增量的"while"循环,行为也会相同。前/后的性质只适用于当前的表达-人们似乎对理解范围有很大的问题。为什么您认为"for(int i=0;i
- @肯·贝克特:可能是因为我只是从左到右看。但是,如果他们的行为相同,为什么这是一个坏的做法?除了引起困惑之外(我的问题是一个很好的证明)?
- @Giammin:我现在意识到,对于许多开发人员来说,这是非常基础的知识,但对于其他许多开发人员来说,这显然是一些新的/有趣的东西。我不明白为什么有些人觉得这个问题令人反感。StackOverflow充满了简单和不那么简单的问题。没什么害处。
- @彼得,你误会我了。我觉得你的问题没有冒犯性。我认为每个问题都是合法的。我只是没想到这么多开发人员没有意识到这一点。因此,你的问题是不合法的。
- @吉安明:好吧,干杯:)这的确是我和其他人所不知道的语言的一个基本部分。
- 这段代码不仅很难阅读,而且还有意想不到的副作用。在生产中使用这样的代码是一个非常糟糕的主意。我希望没有人会想到在他们的代码中使用它。
- 你不会写那个代码的
- 可能是x=x++;与x++;的区别是什么?
- 这种声明是合法的,这一事实表明C语言及其后代有多错误。
这是:
正如你所做的(以下是一个过于简单化的问题):
1 2 3
| int i = 0;
i = i + i; // i=0 because the ++ is a postfix operator and hasn't been executed
i + 1; // Note that you are discarding the calculation result |
实际发生的事情比这更复杂-看一下msdn,7.5.9 postfix递增和递减操作符:
The run-time processing of a postfix increment or decrement operation of the form x++ or x-- consists of the following steps:
请注意,由于优先顺序的原因,后缀++出现在+=之前,但结果最终未被使用(使用了i的先前值)。
把i += i++分解成它的组成部分需要知道+=和++都不是原子的(也就是说,两者都不是单一的操作),即使它们看起来是原子的。这些方法的实现涉及到临时变量,在操作发生之前的i的副本-每个操作一个。(我将分别使用iAdd和iAssign作为++和+=的临时变量)。
因此,更接近正在发生的事情是:
1 2 3 4 5 6
| int i = 0;
int iAdd = i; // Copy of the current value of i, for ++
int iAssign = i; // Copy of the current value of i, for +=
i = i + 1; // i++ - Happens before += due to order of precedence
i = iAdd + iAssign; |
- +因为即使我现在也明白了
- @oded在语句评估完成之前完成++操作。因此,+=覆盖该值。这是怎么回事?
- @实际上是:int i = 0; i = i + 1; (postfix) i = 0; (assignment)。如果你在那句话的其他地方使用了i,那么它的值将为1。
- @CThulhu——本质上。DTB的答案涉及到细节。
- 看起来这个应该被接受为正确答案,因为它是基于一个标准的(同样的规范也在这里:ecma-international.org/publications/standard s/ecma-334.htm)。
- 我不买这个。@yory的回答更准确。首先,在你的回答中,你说最后一行是i+1,而应该是i=i+1。这不是i++所说的吗?
- @隐士-你说得很对。为了说明++和+=不是原子的,我过于简单化了。
- 答案的第一部分是多余的。上一个代码示例可能已经完成了。不过1。
- 解释得很好,谢谢你!
- 最后一个例子应该显示"int iadd=i";和"int iasign=i;"。
- @肯贝克特-是的,你是对的。更新答案。
运行代码的反汇编:
1 2 3 4 5 6 7 8 9 10 11 12
| int i = 0;
xor edx, edx
mov dword ptr i, edx // set i = 0
i += i++;
mov eax, dword ptr i // set eax = i (=0)
mov dword ptr tempVar1, eax // set tempVar1 = eax (=0)
mov eax, dword ptr i // set eax = 0 ( again... why??? =\ )
mov dword ptr tempVar2, eax // set tempVar2 = eax (=0)
inc dword ptr i // set i = i+1 (=1)
mov eax, dword ptr tempVar1 // set eax = tempVar1 (=0)
add eax, dword ptr tempVar2 // set eax = eax+tempVar2 (=0)
mov dword ptr i, eax // set i = eax (=0) |
等效码
它编译为与以下代码相同的代码:
1 2 3 4 5 6
| int i, tempVar1, tempVar2;
i = 0;
tempVar1 = i; // created due to postfix ++ operator
tempVar2 = i; // created due to += operator
++i;
i = tempVar1 + tempVar2; |
第二个代码的反汇编(只是为了证明它们是相同的)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| int i, tempVar1, tempVar2;
i = 0;
xor edx, edx
mov dword ptr i, edx
tempVar1 = i; // created due to postfix ++ operator
mov eax, dword ptr i
mov dword ptr tempVar1, eax
tempVar2 = i; // created due to += operator
mov eax, dword ptr i
mov dword ptr tempVar2, eax
++i;
inc dword ptr i
i = tempVar1 + tempVar2;
mov eax, dword ptr tempVar1
add eax, dword ptr tempVar2
mov dword ptr i, eax |
打开反汇编窗口
大多数人不知道,甚至不记得,他们可以使用Visual Studio反汇编窗口看到最终的内存汇编代码。它显示正在执行的机器代码,而不是CIL。
在调试时使用:
Debug (menu) -> Windows (submenu) -> Disassembly
那么,postfix++发生了什么?
postfix++告诉我们要在计算之后增加操作数的值…所有人都知道…有点困惑的是"评估后"的含义。
那么,"评估后"是什么意思呢?
- 操作数在同一行代码上的其他用法必须受到影响:
- a = i++ + i第二个i受增量影响
- Func(i++, i)第二个I受到影响
- 同一线路上的其他用途涉及短路操作器,如||和&&:
- (false && i++ != i) || i == 0第三个i不受i++影响,因为它没有被评估
那么:i += i++;的意思是什么?
与i = i + i++;相同
评估顺序为:
存储I+I(即0+0)
增量i(i变为1)
将步骤1的值赋给i(i变为0)
不是增量被丢弃。
i = i++ + i;的意思是什么?
这与前面的示例不同。第3个i受增量影响。
评估顺序为:
第一店(即0店)
增量i(i变为1)
存储步骤1+i的值(即0+1)
将步骤3的值赋给i(i变为1)
- +1++—用于纯粹的硬核解剖。查克·诺里斯会感到骄傲的:)我猜你是在假设操作是在英特尔上的,而不是单端口的…
- C为表达式定义了一个明确的计算顺序,而对象代码只是实现该顺序。机器代码输出不是评估订单的原因或解释。
- 机器代码使我们很容易理解如何实现IMO的评估顺序。
- @斯图尔特,我知道你在那里做了什么。不过,对于被抛弃的上票,我感到很遗憾。
- @卡兹:那么,对此有什么解释呢?编译器是一种人造的工作…没有自然(如物理或数学)可以解释。这完全没有理由。他们可以实现它,这样在这种情况下,结果将是1,而不会影响所有其他普通的postfix++运算符用法。
- 有些矛盾:如果a = a + b与a = b + a相同…那么为什么a = a + a++与a = a++ + a不一样呢?真正的问题是…C认为什么是一条指令?
- 机器代码只显示评估方案的一些内容,因为它没有被优化。优化后的机器代码只会使i的初始化为零,而不会让您更明智。假设甚至使用了i。否则,i可能会完全消失。如果使用i(例如打印其值),编译器可以用立即零操作数替换出现的i,确保i在任何地方都不被修改(在i += i++优化后)。
- a++ + a与a + a++不同,因为它不再是纯数学。代数中的交换律不考虑变量在表达式中间改变值的可能性。当编程是函数式编程时,数学只能巧妙地映射到编程。甚至没有,因为代表性的限制。例如,浮点数有时表现得像实数,有时则不是。即使没有副作用,交换性和结合性定律,适用于数学中的实数突破浮点数。
- 优化是为了使事情更快…不要改变代码的含义…因此,当我们讨论代码的含义时,调试代码的分析是有效的。
- @卡兹:你说得对,埃多克斯1〔7〕…postfix++似乎会影响操作之后的每个a,即使在同一行代码中也是如此。
- 我的答案中包含了这一点。这只是一个严格的自下而上的,左右评价。a++ + a中的+运算符完全计算左侧操作数,包括其所有副作用,然后计算右侧操作数。
- 耶!C在这件事上似乎工作得很好……它尊重懒惰的评价者,并与Func(i++, i++, i++, i++)等功能一起工作。推送每个参数后,i++操作就会生效。
- 发表了另一个关于这个问题的更具c_ish分析的答案:stackoverflow.com/a/13535004/195417
- +1,回答得很好。是否第二次将eax设置为i(0)(在这里输入"为什么?")因为一些中间语言,C被编译成?
- 贝恩:不…这仅仅是因为我没有意识到代码没有被编译器优化。有两种分配:到tempvar1和到tempvar2,设置为相同的内容…但非优化代码,不在乎它们是否相同。
- 所有这些混淆和一些争论都指向显而易见的问题:++和--操作符都是坏主意,一开始就不应该包含在语言中!他们是C的遗留物,在C也是个坏主意。我认为鲁比做出了拒绝他们的正确选择。它们令人困惑,使代码变得不必要的复杂。我同意递增/递减声明:独立的i++或i--。但是允许他们做操作员是一个错误。
评估如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| Stack <int> stack = new Stack <int>();
int i ;
// int i = 0;
stack .Push(0); // push 0
i = stack .Pop(); // pop 0 --> i == 0
// i += i++;
stack .Push(i ); // push 0
stack .Push(i ); // push 0
stack .Push(i ); // push 0
stack .Push(1); // push 1
i = stack .Pop() + stack .Pop(); // pop 0 and 1 --> i == 1
i = stack .Pop() + stack .Pop(); // pop 0 and 0 --> i == 0 |
也就是说,i改变了两次:一次由i++表达式改变,一次由+=语句改变。
但是+=语句的操作数是
- 评估i++前的值i(+=左侧)和
- 评估i++前的值i(+=右侧)。
- 啊,这是一个很好的解释。这让我想起了我使用反波兰符号处理基于堆栈的计算器时的情景。
首先,i++返回0。然后,i增加1。最后,将i设置为i的初始值,即0加上i++返回的值,即0。0±0=0。
- 但它是i += i++;,不是i = i++;,所以i++0的值加在i上,而不是"i的值设为i++的返回值"。现在的问题是,当把返回到i的值i++相加时,i是递增值还是非递增值?我的朋友,答案写在说明书上。
- 是的,我会修好的。但无论如何,由于i = 0最初,i += something相当于i = 0 + something即i = something。
这只是抽象语法树的从左到右自下而上的评估。从概念上讲,表达式的树是自上而下遍历的,但是当递归从树的底部弹出时,计算就会展开。
1 2 3 4 5 6 7 8 9 10
| // source code
i += i++;
// abstract syntax tree
+=
/ \
i ++ (post)
\
i |
评估首先考虑根节点+=。这是表达式的主要组成部分。必须对+=的左操作数进行计算,以确定存储变量的位置,并获得先验值为零。接下来,必须对右侧进行评估。
右侧是后递增的++运算符。它有一个操作数,即i,它既是一个值的源,又是一个存储值的地方。操作员评估i,找到0,然后将1存储到该位置。根据返回优先值的语义,返回优先值0。
现在控制权回到了+=操作员手中。它现在拥有完成操作所需的所有信息。它知道存储结果的位置(i的存储位置)和优先值,并且它有附加到优先值的价值,即0。所以,i以零结束。
与Java一样,C语言通过固定评估的顺序,对C语言的一个非常细微的方面进行了清理。从左到右,自下而上:编码人员可能期望的最明显的顺序。
- +1:我同意你的看法,除了每个编码员都希望…我想应该是这样的:SetSum(ref i, Inc(ref i))和int SetSum(ref int a, int b) { return a += b; }和int Inc(ref int a) { return a++; }。当然,我不再期待。
- 而且,我所期望的是不一致的!它不等于Set(ref i, Sum(i, Inc(ref i)))和int Set(ref int a, int b) { return a = b; }和int Sum(int a, int b) { return a + b; }。
- 谢谢,你暗示我的回答有缺陷/不完整,我必须改正。
- SetSum的问题是它不计算左操作数i的值,而只计算其地址,因此它不等于对操作数进行从左到右的完整计算。你需要像SetSum(ref i, i, PostInc(ref i))这样的东西。SetSum的第二个参数是要添加的值,我们只使用i来指定i的优先值。SetSum只是int SetSum(ref int dest, int a, int b) { return dest = a + b; }而已。
- 混淆发生(至少对我来说)与+=运算符,因为赋值运算符具有从右到左的计算(例如a=b=c=d)…所以可以想象,+=遵循同样的规则,作为一个原子操作(就像我用setsum方法所做的那样)。但事实上,C将a += b翻译成a = a + b…显示+=运算符不是原子的…它只是句法上的糖分。
- 赋值具有从右到左的关联性,但操作数的计算仍然可以从左到右。因此,如果a += b有从左到右的评估,在继续进行b之前,它应该提升a的值并记住它。在像a = b这样的直接任务中,我们不需要a的先验值,这样就不会出现这种情况。
- +1代表ASCII艺术
因为i++首先返回值,然后递增。但当我设为1后,你将它设回0。
后增量方法如下所示
1 2 3 4 5 6
| int ++(ref int i)
{
int c = i;
i = i + 1;
return c;
} |
所以基本上,当你调用i++时,i是递增的,但在你的例子中,原值是返回的,它是0。
i++的意思是:返回i的值,然后递增。
I+= i++表示:取i的当前值。添加i++的结果。
现在,我们把i=0作为一个开始条件。i+=i++现在的计算方法如下:
我现在的价值是多少?它是0。存储它,以便我们可以将I++的结果添加到其中。
计算i++(计算为0,因为这是i的当前值)
加载存储值并将步骤2的结果添加到该值中。(加0到0)
注意:在步骤2的末尾,i的值实际上是1。但是,在步骤3中,您可以通过在i递增之前加载它的值来丢弃它。
相对于i++,++i返回递增值。
因此,我给你1。
简单答案
1 2 3 4 5 6 7 8
| int i = 0;
i += i++;
// Translates to:
i = i + 0; // because post increment returns the current value 0 of i
// Before the above operation is set, i will be incremented to 1
// Now i gets set after the increment,
// so the original returned value of i will be taken.
i = 0; |
后固定增量运算符++在表达式中为变量提供一个值,然后再次对i执行您分配的增量,该增量将返回零(0)值,从而覆盖递增的一(1),因此您将得到零。您可以在++operator(msdn)中阅读有关递增运算符的更多信息。
C在做什么,以及困惑的"为什么"
我还期望值是1…但对这一问题的一些探索确实澄清了一些要点。
按以下方法排序:
1 2 3
| static int SetSum(ref int a, int b) { return a += b; }
static int Inc(ref int a) { return a++; } |
我希望i += i++与SetSum(ref i, Inc(ref i))相同。此语句后i的值为1:
1 2 3
| int i = 0;
SetSum(ref i, Inc(ref i));
Console.WriteLine(i); // i is 1 |
但后来我得出另一个结论…i += i++实际上与i = i + i++相同。所以我创建了另一个类似的例子,使用这些函数:
1 2 3
| static int Sum(int a, int b) { return a + b; }
static int Set(ref int a, int b) { return a = b; } |
调用此Set(ref i, Sum(i, Inc(ref i)))后,i的值为0:
1 2 3
| int i = 0;
Set(ref i, Sum(i, Inc(ref i)));
Console.WriteLine(i); // i is 0 |
这不仅解释了C在做什么……但是为什么很多人对它感到困惑…包括我在内。
- 请将此添加到原始答案中,将其作为单独的答案不会增加任何好处。
- 我这样做不是为了反驳另一个答案,因为它是关于反编译代码的…在这篇文章中,我尝试了一种不同的方法来解释事情。你怎么认为?我应该编辑另一个答案并附加这个答案吗?也许,在这之前……不知道!谢谢你的建议!
++postfix在递增之前对i进行评估,+=只对i进行一次评估。
因此,0+0=0,因为i是在递增之前进行评估和使用的,因此使用++的后缀格式。要先递增i,请使用前缀形式(++i)。
(另外,只需注意:您应该只得到1,因为0+(0+1)=1)
参考文献:http://msdn.microsoft.com/en-us/library/sa7629ew.aspx(+=)http://msdn.microsoft.com/en-us/library/36x43w8w.aspx(++)
i += i++;等于零,因为它在后面执行++。
i += ++i;会先做的
我一直记得的一个好的助记法是:
如果++在表达式后面,则返回它之前的值。所以下面的代码
1 2
| int a = 1;
int b = a++; |
是1,因为a在增加之前是1,而a之后是11。人们称之为后缀符号。还有一个前缀表示法,情况正好相反:如果++位于前面,则表达式返回操作之后的值:
1 2
| int a = 1;
int b = ++a; |
b是这里的两个。
所以对于你的代码来说,这意味着
i++返回0(如上所述),所以0 + 0 = 0返回。
1
| i += (++i); // Here 'i' would become two |
Scott Meyers描述了这两个符号在"有效C++编程"中的区别。在内部,i++(postfix)记住i的值,调用前缀符号(++i并返回旧值i。这就是为什么你应该在for循环中使用++i(尽管我认为所有现代编译器都在把i++转换为++i在for循环中)。
- 我测试了int i = 0; i += (++i),i设置为1而不是2。对我来说也是有意义的,因为使用前缀而不是后缀并不能改变这样的事实:如果你把i += (++i)写出到i = i + (++i),i在++i之前被评估,结果是i = 0 + (++i)最终是i = 0 + 1。
你的问题唯一正确的答案是:因为它是未定义的。
好吧,在你们烧死我之前……
你们都回答了为什么i+=i++是正常的,并且合乎逻辑地导致i=0。
我很想对你的每一个答案投反对票,但是我计算出的名声太高了。
为什么我对你们这么生气?不是因为你的答案所解释的。
我的意思是,我读到的每一个答案都做出了非凡的努力来解释不可能的事情,我鼓掌!< BR>
但是结果是什么呢??是直观结果吗?是可接受的结果吗??
你们每个人都看到了"裸王",不知何故都把它当作一个理性的国王。
你们都错了!
i+=i++;导致0未定义。
语言评估机制中的一个错误,如果您愿意的话..甚至更糟!设计上的错误。
需要证据吗?你当然想要!
int t=0; int i=0; t+=i++; //t=0; i=1
现在这个…是直观的结果!因为我们首先对t进行了评估,给它赋了一个值,只有在评估和分配之后,我们才有了操作后的发生,这是合理的,不是吗?
i=i++和i=i对i产生相同的结果,这是合理的吗?
而t=i++和t=i对i有不同的结果。
post操作应该在语句评估之后发生。
因此:
如果我们写下:
因此,同:
1 2 3
| int i=0;
i= i + i;
i ++; |
因此,同:
1 2 3
| int i=0;
i = i + i;
i = i + 1; |
任何不是1的结果都表明编译器中有一个bug,或者如果我们进行理性思考的话,语言设计中有一个bug——然而,msdn和许多其他来源告诉我们"嘿——这是未定义的!"
现在,在我继续之前,任何人都不支持或承认我给出的这组示例。然而,这正是根据直觉和理性的方式应该得到的结果。
编码人员不应该知道如何编写或翻译程序集!
如果它是以一种不尊重语言定义的方式编写的,那就是一个错误!
最后,我从维基百科复制了这个,递增和递减操作符:
由于递增/递减运算符修改其操作数,因此在同一表达式中多次使用此类操作数可能会产生未定义的结果。例如,在x等表达式中?++x,不清楚应该按什么顺序执行减法和增量运算符。当编译器应用优化时,这种情况会变得更糟,这可能导致操作的执行顺序与程序员预期的不同。
因此。
正确的答案是不应该使用这个!(因为它是未定义的!)
对。。-即使C编译器试图以某种方式将其规范化,它也会产生不可预测的结果。
我没有找到任何关于C描述行为的文档,所有人都被记录为语言的正常或定义良好的行为。我发现恰恰相反!
[从msdn文档中复制了postfix递增和递减运算符:++和--]
当对函数参数应用后缀运算符时,参数的值不保证在传递给函数之前递增或递减。请参阅C++标准中的1.1.17节以获取更多信息。
注意那些没有保证的话…
原谅我,如果这个答案看起来傲慢-我不是一个傲慢的人。我只是认为,成千上万的人来到这里学习,我读到的答案会误导他们,并会损害他们的逻辑和对这个主题的理解。
- 我不确定我是在跟踪100%,但是你引用C++文档,但是我的问题是关于C++的。相关文件在这里。
- 我在回答中提到了C。从您提供的链接中:x++或x的结果——是操作前x的值,而+x或--x的结果是操作后x的值。在这两种情况下,X本身在操作后具有相同的值。很明显,测试时不是这样的。因为i=++i将提供与i=i++不同的结果。所以我的回答是正确的。
- AHA,好吧,但它是混淆的,因为你参考C++文档。那么您要说的是,规范没有得到正确的实现?
- 不,我要说的是,根据规范,它是未定义的,使用未定义将导致未定义的结果。
- 在C++中未定义,但C表示它在操作之后应该是相同的值,不是吗?这与未定义不同(但我同意你不应该使用它,看我的免责声明,我只是想了解发生了什么)。
- @彼得,它在C中也没有定义。C定义不包括您提供的案例-并且我所显示的测试案例严格显示结果与预期不同。不管事实如何,我们都能看到发生了什么事情来产生这样的结果。没有人保证结果永远是可靠的。这就是它未定义且不应使用的原因。但是,即使我们可以保证它可以保持稳定——正如您已经提到的——它也不应该被使用。
- "语言评估机制中的一个错误,如果您愿意的话..甚至更糟!未定义的行为不是错误。这是标准的一个有意的部分,留给将来的实现,以防硬件发生变化,并允许更好的优化。由于ub,编译器可以假定,例如,数组访问不可能越界,或者如果指针被取消引用,那么它不可能是NULL。这使得编译器能够以牺牲安全为代价生成更高效的代码,而安全性则由程序员来决定。
变量后的+运算符使其成为后缀增量。递增发生在语句中所有其他内容之后,即添加和赋值。相反,如果您将++放在变量之前,它将在计算i的值之前发生,并给出预期的答案。
- ++不是发生在+=语句之后,而是发生在+=语句执行期间。这就是为什么++的效果被+=覆盖的原因。
- 使用++我实际上得到的是1,而不是2(我最初的"预期答案")。
- 似乎赋值+=覆盖了由于表达式中的前或后增量而进行的修改。
计算步骤如下:
int i=0//初始化为0
i+=i++//方程
i=i+i++//编译器简化方程后
i=0+i++//i值替换
i=0+0//i++为0,如下所述
i=0//最终结果i=0
这里,EDOCX1的初始值(0)是0。wkt,i++只不过是:首先使用i值,然后将i值增加1。所以它使用i值0,同时计算i++并将其递增1。所以它的结果是0。
上面的答案有很多很好的推理,我只是做了一个小测试,想和大家分享一下
这里的结果是0。现在考虑以下情况:
案例1:
之前我认为上面的代码类似于这个,所以乍一看答案是1,而我对这个的真正答案是1。
案例2:
1
| i = i + i++; //Answer 0 this resembles the question code. |
这里增量运算符不在执行路径中,与前面的情况不同,i++有机会在添加之前执行。
我希望这能有所帮助。谢谢
要非常小心:阅读C FAQ:你要做的(混合赋值和同一变量的++)不仅是未指定的,而且是未定义的(这意味着编译器在评估时可以做任何事情)。不仅给出"合理"的结果)。
请阅读第3节。整个章节值得一读!尤其是3.9,这解释了未指明的含义。第3.3节简要总结了"i++"和类似的功能。
根据编译器内部的不同,您可能会得到0、2、1,甚至其他任何东西!因为它是未定义的,所以他们可以这样做。
- 哎呀,C…我被"gcc"抛弃了,有些人通过它来反汇编代码。
- 我错过了也是C,但还是喜欢这个答案。
- @伊恩:谢谢,我也相信这是值得保留的答案,许多人不知道这一点(或关于伟大的常见问题解答,从用户网的最佳时间,大部分了解子网的人将要去同一个地方更新它)
有两种选择:
第一个选项:如果编译器按如下方式读取语句,
结果是2。
为了
结果是1。
简单地说,
i++,将在"+="运算符完成后向"i"添加1。
您需要的是++i,这样它将在执行"+="运算符之前向"i"添加1。
希望从C编程101的角度来回答这个问题。
在我看来,事情是按这样的顺序发生的:
i的计算结果为0,导致i = 0 + 0与增量操作i++"排队",但0对i的赋值也没有发生。
增量i++发生
上面的任务i = 0发生了,有效地覆盖了2(后增量)应该做的任何事情。
现在,2可能永远不会发生(可能不会发生?)因为编译器可能意识到它没有任何作用,但这可能与编译器有关。无论哪种方式,其他更为丰富的答案都表明,结果是正确的,符合C标准,但它没有定义在这里发生的C/C++。
如何以及为什么超出了我的专业知识范围,但是之前评估的右手边分配发生在后增量之后,这一事实可能让我困惑。
此外,你不会期望结果是2不管,除非你做了++i而不是i++,我相信。
- 预增量版本产生EDOCX1 18的结果,用C++:IDENNE.COM/8DH8TF
- 这是有道理的。但是,与后增量相比,前增量是一个稍微不复杂的情况。
1 2 3 4 5 6 7
| i=0
i+=i
i=i+1
i=0; |
然后将1添加到EDOCX1中(0)。
i+= i++
因此,在将1添加到i之前,i的值为0。只有在前面加1,i才得到0。
答案是i将是1。
让我们看看如何:
最初是i=0;。
然后根据值计算i +=i++;时,我们会得到类似0 +=0++;的值,因此根据运算符优先级,0+=0将首先执行,结果将是0。
然后将增量运算符应用于0++、0+1,i的值将为1。
- 这个答案是错误的。你不会得到1,因为当你做0 += 0++;的时候,赋值是在增量++之后的,但是我在++之前解释了它的值(因为它是后运算符)。
- 抱歉,这是错误的。读我的问题,你会看到我说结果是0。如果运行代码,您将看到它实际上是0。