Java 关于for(),为什么使用i++而不是++i?

Java 关于for(),为什么使用i++而不是++i?,java,php,c++,c,Java,Php,C++,C,也许在编译器优化后,它对编译器来说并不重要,但在C/C++中,我看到大多数人以以下形式创建for循环: for (i = 0; i < arr.length; i++) 其中,增量是使用post fix++完成的。我知道这两种形式的区别。i++返回i的当前值,但在安静状态下将i加1++我首先将1添加到i,并返回比原来多1的新值 我认为I++需要更多的工作,因为除了下一个值之外,还需要存储上一个值:Push*&I到stack或load到register;增量*&i。与++i:Increme

也许在编译器优化后,它对编译器来说并不重要,但在C/C++中,我看到大多数人以以下形式创建for循环:

for (i = 0; i < arr.length; i++)
其中,增量是使用post fix++完成的。我知道这两种形式的区别。i++返回i的当前值,但在安静状态下将i加1++我首先将1添加到i,并返回比原来多1的新值

我认为I++需要更多的工作,因为除了下一个值之外,还需要存储上一个值:Push*&I到stack或load到register;增量*&i。与++i:Increment*&i;然后根据需要使用*&i

我发现增量*&I操作可能涉及寄存器负载,这取决于CPU设计。在这种情况下,i++需要另一个寄存器或堆栈推送

无论如何,在什么时候,为什么我变得更时尚

我倾向于相信阿什格洛夫:这是一种教育学的东西,因为我们大多数人在Windows或*nix系统上使用C/C++,在那里编译器的质量很高,所以没有人会受到伤害


如果您使用的是低质量的编译器或解释环境,则可能需要对此敏感。当然,如果你正在做高级C++或设备驱动程序或嵌入式工作,希望你有足够的经验,这不是什么大不了的。狗有佛性吗?谁真的需要知道?

< p>我认为随着C++的创建,C++变得更加流行,使你可以调用非平凡对象上的++。
好的,我详细说明一下:如果你调用I++并且I是一个非平凡的对象,那么在增量之前存储一个包含I值的副本将比指针或整数更昂贵。

使用哪一个并不重要。在一些非常过时的机器上,在C++的某些情况下,++i更有效,但是现代编译器不存储结果,如果没有存储。至于何时开始流行对for循环进行后期增量,我的《K&R第二版》的副本在第65页使用了我翻阅时发现的第一个for循环。

对于整数类型,如果不使用表达式的值,这两种形式应该是等效的。这在C++世界中不再是复杂的,但是在语言名中保存。


我怀疑I++在早期变得更加流行,因为这是原始K&R的C编程语言书中使用的风格。你必须问他们为什么选择那个变体。

在某种程度上,这是惯用的C代码。事情通常都是这样做的。如果这是您最大的性能瓶颈,那么您可能正在解决一个独特的问题


然而,看看我的K&R C编程语言第一版,我发现循环中的第一个实例pp 38使用的是++I,而不是++。

我认为我的前任对于选择后增量而不是预增量的副作用是正确的

因为它的时尚性,它可能很简单,因为你在语句中以相同的重复方式启动所有三个表达式,这是人类大脑似乎倾向于做的事情。

出于某种原因,i++在C中变得更加惯用,尽管它创建了一个不必要的副本。我认为这是通过K&R实现的,但我在其他答案中看到了这一点。但我不认为C语言有性能上的差异,因为它只在内置程序上使用,编译器可以优化复制操作


<>它确实在C++中有所不同,但是,我可能是一个用户定义类型,操作符+ +被重载。编译器可能无法断言复制操作没有可见的副作用,因此可能无法消除它

其他人告诉你的主要规则是:始终如一。选择一个,不要使用另一个,除非是一个特定的情况。

< P>我为什么理论认为I++更流行的是,当人们学习C或C++时,他们最终学会了像这样迭代代码:

while( *p++ ) {
    ...
}
请注意,这里的post-fix表单很重要,使用中缀表单会产生一次性的bug

当需要编写一个for循环时,如果++i或i++实际上并不重要,那么使用后缀形式可能会感觉更自然

补充:我上面写的内容适用于基本类型,真的。当使用基本类型编写代码时,您倾向于快速完成任务,并执行自然发生的操作。这是我需要对我的理论提出的重要警告


如果C++是C++类上的一个重载操作符,那么在注释中建议的可能性Realk K当然需要对包含此类类的循环进行极端的处理,而不是简单地做一些简单的事情。在跳转到开始之前,需要重新加载缓存中的值以增加它。


你不需要的是++i,不需要缓存移动。

追溯到K&R,我看了它的前身:~1975年。这里是e的前几段
示例使用++n。但是每个for循环都使用i++。所以要回答你的问题:几乎从一开始i++就变得更加时尚。

因为一旦你开始使用++i,人们就会感到困惑和好奇。他们将停止那里的日常工作,开始用谷歌搜索解释。12分钟后,他们将进入stack overflow并创建这样一个问题。瞧,你的雇主又花了10美元。至于原因,下面是K&R对这个问题的看法:

布莱恩·柯林汉 你得问问丹尼斯,也许是在办公室里。我有一个 暗淡的内存表明它与中的增量后操作有关 pdp-11,尽管除此之外我不知道,所以不要引用我的话

<>在C++中,迭代器的首选样式实际上是++i,用于一些微妙的 实施原因

丹尼斯·里奇 没有什么特别的原因,它只是变得流行起来。生成的代码 在PDP-11上是相同的,只是一条inc指令,没有自动增量

霍普纸 汤普森更进一步,发明了++和-运算符,可以递增或递减;它们的前缀或后缀位置决定更改是在记录操作数的值之前还是之后发生。它们不是B的最早版本,而是沿途出现的。人们经常猜测,它们是使用DEC PDP-11提供的自动递增和自动递减地址模式创建的,而C和Unix最初是在DEC PDP-11上流行的。这在历史上是不可能的,因为开发B时没有PDP-11。然而,PDP-7确实有一些“自动递增”的内存单元,其特性是通过它们的间接内存引用使单元递增。这一特性可能向汤普森暗示了这样的操作符;使它们同时成为前缀和后缀的概括是他自己的。事实上,自动递增单元并没有直接用于运算符的实现中,而且更强大 创新的动机可能是他观察到的++x的翻译比x=x+1的翻译小


在C语言中,除了前缀inc/dec外,所有导致变量具有新值的运算符都会修改左侧变量i=2、i+=5等。因此,在++i和i++可以互换的情况下,许多人更习惯于使用i++因为运算符位于右侧,修改左侧变量


如果第一句话不正确,请告诉我,我不是C方面的专家。

似乎你对差异有着非常坚定的理解,因此我认为差异是基于个人偏好。越来越多的人选择了i++,现在它几乎已经成为for循环的一个不言而喻的标准:無 穆就像我说的那样;不需要任何工作任何值得作者花费时间编写的编译器它将编译成完全没有代码的代码,如果从未使用i的值,则在增加i之前存储i的值是没有任何工作的。你会期待我++我是否比++i;慢;?如果不是,为什么你会期望i++;慢一点?取决于你问谁;我看到了我经常使用的+,它是我默认编写的版本。除非我知道在递增I之前需要I的值,否则我不会选择I++。@Rich:不要混淆表达式的结果和它的副作用。++i的结果是i加1的当前值;它的副作用是我得到了递增。副作用不必立即应用;它只需由下一个序列点应用。在具有多个副作用(如x=++i*j++)的表达式中,对i和j的更新可能会延迟到乘法和赋值到x之后。关于在for循环中使用++i或i++,没有区别;智能编译器将为两者生成相同的代码。++可以被称为后缀或前缀,而不可以理解。答案是:+ +作为C++中的前缀可以修改原始对象,而如果C++中的后缀为+++,则必须复制原始对象的副本作为值,如果使用该值。所有这些都与C语言无关,C语言是被问及这个问题的语言。更不用说创建一个对象也可能需要相当长的时间。我的也是!大约十年前,我在K&R风格中使用了++I,一位同事问这是什么意思?从那以后,我一直对这个++I/I++的东西很好奇。它不会创建不必要的副本。你把C++思想应用到C,这是不正确的。表达式的计算不涉及任何副本,且没有副作用。你会期待我;花点时间或生成代码?如果不是,为什么我要++;比++i;花费更多的时间或生成更多的代码;?如果变量“i”是可变的,则语句i;必须生成代码。在简单的编译器上,简单地认为一切都是易变的,语句i;仍然会生成代码。注意,如果i是可变的,那么语句a=i++;a=++i;两人都必须准确地读i一次;在大多数体系结构上,两者都不能使用直接输入-
放置增量。@R..:我不知道你在敲什么。i++的结果是增量之前i的值。为了返回结果,我必须被复制到某个地方。这是否可以在硬件上实现并不重要。结果仍然必须存储在某个地方。如果表达式的结果被丢弃,编译器可能会消除这种复制。如果I是int,我无法想象编译器不这样做。@sbi:并且++I的值是增量加1之前I的值,为了返回该结果,必须将I+1的值复制到某个地方。向i中实际添加1的副作用不必立即应用。在循环控制表达式的上下文中,一个相当聪明的编译器不会费心存储这两种形式的结果。@R:在我使用的嵌入式系统编译器上,volatile强制内存读取只执行一次,如果“volatile”变量链接到I/O端口的地址,这种行为通常是绝对必要的。虽然我通常不喜欢这样的设计,但在许多I/O设备上,读做了很多事情。例如,在许多UART上,如果数据字节可用,则read将检索数据并将其从队列中删除。如果不能保证每次读取都精确地发生一次,那么用C语言为这些设备编写代码是不可能的。也许是一个实现细节,但却是一个常见的问题。既然OP问的是什么时候,这个问题似乎是最终的答案。我问的是什么时候,也许K&R第二版是什么时候。所以这个答案也很好。然而,我也问了为什么,阿什格洛夫直接回答了这个问题。不知何故,在我的脑海里,为什么变得比什么时候更重要。很抱歉我想说,在Nathon和azheglov之间,我得到了令人满意的答案。我的《K&R第一版》的副本似乎在我翻过的十几个页面上以几乎相同的概率使用了++I和I++,这些页面有一个简单递增的for循环。这些示例中没有任何一个文本提到为什么使用前增量或后增量,即使在教程章节或定义for循环的部分中也是如此。顺便说一句,这一部分使用了i++,我想我也可以分享一下,这显示了一个与直觉相反的结果:我想知道你是否理解正确,为什么这是一个流行的习语。这似乎是对的。不过,这让我感到奇怪:这种教学法可能会对一些初级程序员产生负面影响。sbi和Peter G.都指出了运算符++重载和计算成本的问题。@Rick K:同意。我想再写一段关于它的文章,然后为了简洁起见跳过了。编译器可以优化掉递增整数的不需要的副本,但是深度?使用重载的++运算符复制某个类-同意,这是一个不同的游戏。你是说++i在缓存方面比i++更高效吗?我想dzada的意思是,在i++中,你可能会太频繁地增加i一次。是的,我说对于长循环,++i可以在缓存中更高效,这取决于架构,但它的效率不能低于架构。例如,我经常与Sharks tiger、模拟设备、DSP合作,这显然更好。+1将不断提出这个问题的荒谬性融入到实际答案中!这也是我在大学里学习的原因,即I++和-我是PDP 11上的有效堆栈操作-有关详细信息,请参阅上面引用的链接?我试着用谷歌搜索他们,但没有成功。但我确实发现了这个:。根据这个说法,++是肯·汤普森的发明。@reinierpost:谢谢你的维基链接,这是一本有趣的读物。请参阅上面的chist.html完整URL:PDP-7。。。确实有一些“自动递增”的内存单元,其属性是通过它们的间接内存引用使该单元递增。这一特性可能向汤普森暗示了这样的操作符;使它们同时成为前缀和后缀的概括是他自己的。事实上,自动递增单元格并没有直接用于运算符的实现,创新的更大动机可能是他观察到的++x的翻译比x=x+1的翻译小。@Rich:我没有引号的链接:它们来自我的收件箱。