i++;和++;我在C?
如果不使用结果值,i++;和++;我在C?,c,performance,optimization,post-increment,pre-increment,C,Performance,Optimization,Post Increment,Pre Increment,如果不使用结果值,i++和++i之间是否存在性能差异?执行摘要:否 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
之间是否存在性能差异?执行摘要:否
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);
}
我们将编译它们,并获得生成的汇编程序:
$ 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
我的C有点生锈了,所以我提前道歉。就速度而言,我能理解结果。但是,我不明白这两个文件是如何产生相同的MD5散列的。也许for循环运行相同的程序集,但是下面的两行代码不会生成不同的程序集吗
myArray[i++] = "hello";
vs
第一个将值写入数组,然后递增i。然后,我将第二个增量写入数组。我不是汇编专家,但我不知道这两行不同的代码如何生成相同的可执行文件
只要我的两分钱。在C中,如果结果未使用,编译器通常可以将它们优化为相同的
<>但是,在C++中,如果使用其他类型的提供自己的++操作符,前缀版本可能比后缀版本快。因此,如果不需要后缀语义,最好使用前缀运算符。学习Scott Meyers的第6项:区分递增和递减操作的前缀和后缀形式 在对象方面,前缀版本总是优于后缀版本,尤其是在迭代器方面 如果你看一下运营商的呼叫模式,就会发现原因是什么
// Prefix
Integer& Integer::operator++()
{
*this += 1;
return *this;
}
// Postfix
const Integer Integer::operator++(int)
{
Integer oldValue = *this;
++(*this);
return oldValue;
}
看看这个例子,很容易看出前缀操作符如何总是比后缀更有效。因为在使用后缀时需要一个临时对象
这就是为什么当您看到使用迭代器的示例时,它们总是使用前缀版本
但正如您所指出的,对于int,实际上没有什么区别,因为编译器可以进行优化。更好的答案是,
++i
有时会更快,但不会更慢
每个人似乎都认为i
是一种常规的内置类型,例如int
。在这种情况下,将没有可测量的差异
然而,如果i
是复杂类型,那么您可能会发现一个可测量的差异。对于i++
,在递增类之前,必须先复制一个类。根据副本中涉及的内容,它的速度确实可能会较慢,因为使用++it
可以只返回最终值
Foo Foo::operator++()
{
Foo oldFoo = *this; // copy existing value - could be slow
// yadda yadda, do increment
return oldFoo;
}
另一个区别是,使用++i
可以选择返回引用而不是值。同样,根据制作对象副本所涉及的内容,这可能会更慢
一个真实的例子是迭代器的使用。复制迭代器不太可能成为应用程序中的瓶颈,但在结果不受影响的情况下,养成使用
++i
而不是i++
的习惯仍然是一个很好的做法。如果您担心微观优化,这里有一个额外的观察。递减循环“可能”比递增循环更有效(取决于指令集架构,如ARM),前提是:
循环将为以下各项提供一条指令:
i
,设置CPU寄存器状态标志Z==0
)++i
比i++
更有效,这一点并不明显,至少在涉及整数变量时是如此
以及:
因此,我们应该问的问题不是这两个操作中哪一个更快,而是这两个操作中哪一个更准确地表达了您想要完成的任务。我认为,如果您没有使用表达式的值,则没有理由使用I++
而不是++I
,因为没有理由复制变量的值,增加变量,然后丢弃副本
因此,如果不使用结果值,我将使用
++I
。但这并不是因为它更有效:因为它正确地表达了我的意图 请不要让“哪一个更快”成为使用哪一个的决定因素。很可能你永远不会那么在意,而且,程序员的阅读时间比机器时间要昂贵得多
使用对阅读代码的人来说最有意义的方法。我总是喜欢预先递增,但是 我想指出的是,即使在调用operator++函数的情况下,如果函数内联,编译器也能够优化掉临时函数。由于操作符++通常很短,并且通常在头中实现,因此它很可能是内联的 因此,出于实际目的,这两种形式的性能可能没有太大区别。然而,我总是更喜欢pre-increment,因为直接表达我想说的内容似乎比依靠优化器来解决问题要好 另外,给optmizer更少的操作可能意味着编译器运行得更快。@Mark 即使编译器被允许优化掉变量的(基于堆栈的)临时副本,而gcc(在最新版本中)正在这样做, 并不意味着所有的编译器都会这样做 我只是测试了一下
// Prefix
Integer& Integer::operator++()
{
*this += 1;
return *this;
}
// Postfix
const Integer Integer::operator++(int)
{
Integer oldValue = *this;
++(*this);
return oldValue;
}
Foo Foo::operator++()
{
Foo oldFoo = *this; // copy existing value - could be slow
// yadda yadda, do increment
return oldFoo;
}
for (i = 0; i < 100; i++)
for (i = 100; i != 0; i--)
a = ++b + c;
; increment b
LD A, [&b]
INC A
ST A, [&b]
; add with c
ADD A, [&c]
; store in a
ST A, [&a]
a = b++ + c;
; load b
LD A, [&b]
; add with c
ADD A, [&c]
; store in a
ST A, [&a]
; increment b
LD A, [&b]
INC A
ST A, [&b]