K&R似乎更喜欢预增量

我正在通过K&R工作,现在正在练习1-16。 我发现到目前为止,书中只使用了预增量。

大多数其他教程书籍和我见过的源代码往往倾向于后增量,除非有明显的影响,例如while循环等。

这是K&R的风格或技术考虑吗? 或者我只是需要进一步完成本书以获得我的答案?!

这有几个方面。

语义

预增量(减量)和后增量(减量)的语义是不同的。 前者在使用值之前递增一个值,而后者在使用后递增一个值。 例如

unsigned i; for (i=0; i<10; i++ /* ++i */ ) { } 

在这种情况下,无论是使用前增量还是后增量都无关紧要。 这让我想到了第二个方面:

上下文

有时候使用前增量还是后增量都很重要,这取决于使用的上下文。 请考虑以下示例:

 char *p = "some string"; unsigned len = 0; // test 1 while ('\0' != *p++) { len++; } // test 2 (assumes that p points to a non-empty string) while ('\0' != *++p) { ++len; } 

test 1的结果是字符串的长度, test 2的结果是字符串的长度减去1。 因为递增的值p用作表达式的一部分,所以在增量发生时确实很重要:在表达式(测试1)中使用p之后,或者在使用之前(测试2)。 因为len不用作表达式的一部分,所以无论是使用前增量还是后增量都无关紧要。

这让我想到了第三个方面。

履行

为了实现后增量,必须存储p的值,以便以后使用它来递增它,这会为临时值占用额外的存储空间。 预增量不需要临时存储空间。 但是,如果增量值不用作表达式的一部分,那么现代优化编译器能够为前后增量生成相同的代码,其中需要临时值来评估该表达式。

这让我想到了第四个方面(在上一段中略有暗示),但这与C ++有关。

性能

在C ++中,递减和递增运算符可以被重载( 链接 )以使用复杂类型,并且STL( 链接 )确实充分利用它来为其容器类型实现迭代器( 链接 )。 在那种情况下,一个值就像

 set::iterator it; 

可能是一个相当复杂的事情,不仅仅是琐碎的状态(即它不仅仅是一个原始整数)。 在这种情况下,使用预增量++it 确实会对后增量it++因为如果在表达式中使用(例如上下文),则不需要像临时增量所需的那样存储临时值。 这可以节省一些运行时开销。

大约1000万年前,在优化编译器之前,这曾经很重要*。 它真的不再了。 理由是需要额外的store操作才能以天真的方式实现后增量。 虽然编译器大多数都没有以这种方式实现它,所以在给定上下文并信任编译器做正确的事情时,更喜欢哪种样式更有意义。

* JimBalter正确地指出这从来都不是问题,我不应该建议它曾经可能有重要意义。

我会说(即使后增量运算符由于后缀运算符的优先级更高而在标准中排在第一位),预增量是增加对象值的自然方式,因此如果唯一的事情是使用它的一个很好的理由你想要的是增加一个对象的值,

(++E)相当于(E+=1)而不是(E++)