如果不使用结果值,i++和++i之间是否有性能差异?
当前回答
简短的回答:
i++和++i在速度上没有任何区别。一个好的编译器不应该在这两种情况下生成不同的代码。
长一点的回答:
其他答案都没有提到的是,++i和i++之间的区别只在它所找到的表达式中有意义。
对于for(i=0;我< n;i++), i++在它自己的表达式中是单独的:在i++之前有一个序列点,在它之后有一个。因此,生成的唯一机器码是“将i增加1”,并且它是如何与程序的其余部分进行排序的。所以如果你把它改成前缀++,这一点关系都没有,你仍然会得到机器代码“将i增加1”。
++i和i++之间的差异只在数组[i++] = x;与数组[++i] = x;有些人可能会争辩说,后缀在这样的操作中会更慢,因为i所在的寄存器稍后必须重新加载。但是请注意,编译器可以自由地以任何它喜欢的方式对你的指令进行排序,只要它不像C标准所说的那样“破坏抽象机器的行为”。
所以当你假设数组[i++] = x;被翻译成机器代码为:
将i的值存储在寄存器A中。 存储寄存器B中数组的地址。 将A和B相加,将结果存储在A中。 在这个由A表示的新地址上,存储x的值。 在寄存器A //中存储i的值是低效的,因为这里有额外的指令,我们已经做过一次了。 增量寄存器A。 在i中存储寄存器A。
编译器也可以更有效地生成代码,例如:
将i的值存储在寄存器A中。 存储寄存器B中数组的地址。 添加A和B,将结果存储在B中。 增量寄存器A。 在i中存储寄存器A。 ... //其余的代码。
只是因为作为一个C程序员,你被训练成认为后缀++发生在结尾,所以机器代码不需要以这种方式排序。
所以在C语言中,前缀++和后缀++没有区别。现在,作为一个C程序员,你应该知道的是,有些人在某些情况下不一致地使用前缀,而在其他情况下不一致地使用后缀,没有任何理由。这表明他们不确定C语言是如何工作的,或者他们对这门语言的了解不正确。这总是一个不好的迹象,它反过来表明他们在他们的程序中做出了其他有问题的决定,基于迷信或“宗教教条”。
“前缀++总是更快”确实是一个错误的教条,在准C程序员中很常见。
其他回答
请不要让“哪个更快”的问题成为使用哪个的决定因素。你可能永远不会关心那么多,此外,程序员的阅读时间比机器的时间要昂贵得多。
使用任何对阅读代码的人最有意义的方法。
执行摘要:没有。
i++可能比++ I慢,因为I的旧值 可能需要保存以备以后使用,但实际上都是现代的 编译器将对其进行优化。
我们可以通过查看这个函数的代码来证明这一点, 都是++i和i++。
$ cat i++.c
extern void g(int i);
void f()
{
int i;
for (i = 0; i < 100; i++)
g(i);
}
文件是一样的,除了++i和i++:
$ diff i++.c ++i.c
6c6
< for (i = 0; i < 100; i++)
---
> for (i = 0; i < 100; ++i)
我们将编译它们,并获得生成的汇编器:
$ gcc -c i++.c ++i.c
$ gcc -S i++.c ++i.c
我们可以看到生成的对象和汇编程序文件都是相同的。
$ md5 i++.s ++i.s
MD5 (i++.s) = 90f620dda862cd0205cd5db1f2c8c06e
MD5 (++i.s) = 90f620dda862cd0205cd5db1f2c8c06e
$ md5 *.o
MD5 (++i.o) = dd3ef1408d3a9e4287facccec53f7d22
MD5 (i++.o) = dd3ef1408d3a9e4287facccec53f7d22
摘自Andrew Koenig的《效率与意图》:
首先,++i是否比i++更有效还不明显,至少在涉及整型变量时是这样。
和:
所以人们应该问的问题不是这两种操作中哪一种更快,而是这两种操作中哪一种更准确地表达了你想要完成的事情。我认为,如果你不使用表达式的值,永远没有理由使用i++而不是++ I,因为永远没有理由复制一个变量的值,增加变量,然后扔掉拷贝。
因此,如果没有使用结果值,则使用++ I。但不是因为它更有效,而是因为它正确地表达了我的意图。
我总是喜欢预增量,然而……
我想指出的是,即使在调用运算符++函数的情况下,如果函数得到内联,编译器将能够优化掉临时函数。由于操作符++通常很短,并且经常在头文件中实现,因此它很可能被内联。
因此,出于实际目的,这两种形式的性能之间可能没有太大差异。然而,我总是喜欢预增量,因为它似乎更好地直接表达我想说的,而不是依赖于优化器来解决它。
此外,给优化器更少的任务可能意味着编译器运行得更快。
参考Scott Meyers的《更有效的c++第六条:区分增减操作的前缀和后缀形式》。
对于对象,尤其是对于迭代器,前缀版本总是优于后缀版本。
原因是,如果你看一下操作符的调用模式。
// Prefix
Integer& Integer::operator++()
{
*this += 1;
return *this;
}
// Postfix
const Integer Integer::operator++(int)
{
Integer oldValue = *this;
++(*this);
return oldValue;
}
看看这个例子,很容易看出前缀操作符总是比后缀操作符更有效率。因为需要在临时对象中使用后缀。
这就是为什么当你看到使用迭代器的例子时,他们总是使用前缀版本。
但正如你所指出的,对于int型,实际上没有什么区别,因为编译器优化可以发生。
推荐文章
- 如何有效地从数组列表或字符串数组中删除所有空元素?
- 在SQL Server上使用varchar(MAX) vs TEXT
- c++中size_t和int的区别是什么?
- .toArray(new MyClass[0]) or .toArray(new MyClass[myList.size()])?
- 在C和c++中静态变量存储在哪里?
- 是什么导致JNI调用变慢?
- 检查属性是否有属性
- errno线程安全吗?
- 如何快速清除JavaScript对象?
- 如何在C程序中获取当前目录?
- 互斥实例/教程?
- 如何添加一个'或'条件在#ifdef
- extern关键字对C函数的影响
- Node.js vs .Net性能
- 如果使用if-return-return或if-else-return?