+ -
当前位置:首页 → 问答吧 → ++i和i++

++i和i++

时间:2004-09-19

来源:互联网

我曾经在一本书上看到,在单独用 i++ 或者 ++i 的时候,++i 比 i++的执行效率高一些。我想了半天,还是搞不明白。我看linux内核也都用的是i++,所以我就怀疑这个说法了。
有谁知道吗?是不是真的高一些啊?请详细的讲解一下,谢谢!

作者: cxh_nuaa_2001   发布时间: 2004-09-19

i++是先使用i的值后在自增,所以通常的实现为:

temp = i;
++i;
return temp;
很明显,多处了一个中间变量,建立和销毁这个中间变量就是
引起效率低下的原因。

作者: jzh   发布时间: 2004-09-19

这个开销几乎可以不记吧。。。。。

作者: iDay   发布时间: 2004-09-19

开销再少也要占时间和空间吧,你要是执行如
for(i=0; i < 1e6; i++) 等,i++ 和 ++i 的区别就大了。

这个是针对内建的数值对象而言。而且要是要c++,i如果是 某个类的一个对象,想象多一个temp对象的坏处吧。通常就意味着那将是两次拷贝构造函数和两次析构函数的调用。

作者: jzh   发布时间: 2004-09-19

c++里有对象的这种操作吗?

作者: iDay   发布时间: 2004-09-19

内建对象当然直接支持++。
自定义类当然得重载++操作符。

作者: jzh   发布时间: 2004-09-19

经测试得出gcc 在编译的时候会把i++ ++i i+1 编译成都是用同样的累加指令,这个指令比add指令要快。所有这个命题应该是和具体的编译器相关的。

作者: zbw76   发布时间: 2004-09-19

jzh兄,我想知道,对与内建的类型的 ++i 和 i++ 是不是编译后也有
temp = i;
++i;
return temp;

这种情况?C++ 的重载++的情况我知道,但是象一些基本的类型,如int是不是也有上面这种情况呢?

还有,zbw76兄,是不是别的编译器也会按gcc那样编译++i 和 i++呢?

作者: cxh_nuaa_2001   发布时间: 2004-09-20

我觉得对于基本的数据类型,如 int ,i++ 编译时可以先使用 i 变量,然后再 inc i。而不必要有
temp = i;
++i;
return temp;
这个操作,不知对不对。

还有这是不是跟具体的语言或者编译器有关。如 c++,它提供了运算符重载的功能,是不是有影响?

作者: cxh_nuaa_2001   发布时间: 2004-09-20

to chx_nuaa_200:
对于基本数据类型.仅仅只是自增的话,也即
i++;
++i;
生成的汇编指令是一样的。
但可以肯定的是,对于类似赋值性质的语句,如
int num = i++;
int num = ++i;
肯定不一样,前一个语句一定会有一个中间变量来保存i的初始值。
然后i的值才递增。

作者: jzh   发布时间: 2004-09-21

但我昨天写了这么一个程序:
#include <stdio.h>

int main()
{
int i = 3;
i += i++;
printf("i = %d", i);
}

结果输出了
i = 7

请问该怎么解释呢?

作者: cxh_nuaa_2001   发布时间: 2004-09-21

上面的程序还少了一句

return 0;

作者: cxh_nuaa_2001   发布时间: 2004-09-21

先做i++,这时候,i == 3,
后做i +=;这时候这里的i == 4了
所以4 + 3 = 7

作者: iDay   发布时间: 2004-09-21

看看这个就什么都明白了
#include <stdio.h>
int main( )
{
int tmp,i;
tmp = i++;
tmp = ++i;
return 0;
}

gcc翻译成的汇编(添加了说明)
.file "test.c"
.text
.globl main
.type main, @function
main:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
andl $-16, %esp
movl $0, %eax
subl %eax, %esp
第一个操作
movl -8(%ebp), %edx //临时保存i
//i自加1
leal -8(%ebp), %eax
incl (%eax)

movl %edx, -4(%ebp) //赋值给tmp

第二个操作
//i自加1
leal -8(%ebp), %eax
incl (%eax)

//后赋值
movl -8(%ebp), %eax
movl %eax, -4(%ebp)

movl $0, %eax
leave
ret
.size main, .-main
.section .note.GNU-stack,"",@progbits
.ident "GCC: (GNU) 3.3.3 20040412 (Red Hat Linux 3.3.3-7)"

速度应该几乎一样,中间都使用了寄存器中转

作者: zbw76   发布时间: 2004-09-21

上面 iDay 兄的解释我不同意。
--------------------------------
先做i++,这时候,i == 3,
后做i +=;这时候这里的i == 4了
所以4 + 3 = 7
--------------------------------
如果依照你的解释,那么
#include <stdio.h>

int main()
{
int i = 3;
i *= i++;
printf("i = %d", i);
return 0;
}
就应该输出
i = 12
但实际上输出的是
i = 10
虽然 ++ 的优先级高,但不是按你想象的那么执行的。

作者: cxh_nuaa_2001   发布时间: 2004-09-21

i += i++;
应该是先
i=i+1;

i++
看一下下面的程序的汇编表示就清楚了
int main()
{
int i = 3;
i += i++;
return 0;
}
汇编表示
.file "test3.c"
.text
.globl main
.type main, @function
main:
pushl %ebp
movl %esp, %ebp
subl $8, %esp //为变量i分配内存
andl $-16, %esp
movl $0, %eax
subl %eax, %esp
//执行i=3的赋值
movl $3, -4(%ebp)
//下面三个指令执行 i=i+1
movl -4(%ebp), %edx
leal -4(%ebp), %eax
addl %edx, (%eax)

//下面三个指令执行i++
leal -4(%ebp), %eax
incl (%eax)
movl $0, %eax

leave
ret
.size main, .-main
.section .note.GNU-stack,"",@progbits
.ident "GCC: (GNU) 3.3.3 20040412 (Red Hat Linux 3.3.3-7)"

作者: zbw76   发布时间: 2004-09-21

谢谢上面 zbw76 兄的解释。
这么说,i++没有保存临时的对象了。

作者: cxh_nuaa_2001   发布时间: 2004-09-22

没有在堆栈内分配内存,只是临时放在寄存器内。

作者: zbw76   发布时间: 2004-09-22

这样理解正确吗?
当i=3时 i+=i++
先用i做i+=i操作 即i=i+i --> i=3+3=6
再i自增1 即i=6+1

当i=3时 i*=i++
先用i做i*=i操作 即i=i*i --> i=3x3=9
再i自增1 即i=9+1

作者: yonsan   发布时间: 2004-09-28