i++与++i是一种特殊的自增运算,类似的还有自减操作运算i--与--i。下面从C与C++两个角度来比较这两种形式的运算方式。
1.C中:
简单的来说,++i和i++,在作为一个语句单独使用时(如i++;或者++i;),就是 i = i + 1,它们之间没有区别。 而在做为语句的一部分时,a = ++i,相当于 i=i+1; a = i;通过表达式就可以看出,因为++在前,所以是先执行加1操作,再执行赋值操作。而 a = i++,相当于 a = i; i=i+1;++在后,所以是先执行赋值操作,再执行加1操作。
问题:试分析下面的代码在X86/Linux上的输出结果。
代码1:
void main()
{
long i;
i = 0;
printf("%ld\n", (++i)+(++i)+(++i));
}
输出为:7
代码2:
void main()
{
long i;
i = 0;
printf("%ld\n", (++i)+((++i)+(++i)));
}
输出为:9
分析:对于++i来说,实质就是i = i + 1。这个结果应该是保存在寄存器中。那么无论计算顺序为何,都不会影响到最后的计算结果。即代码1的结果应该为6,代码2的结果也应该为6。但是实际的结果为什么为7和9呢?是不是因为寄存器不够而没有放进寄存器中呢?现代计算机,有足够多的寄存器来存放++i的中间结果。唯一的可能就是编译程序就根本没有打算把++i的中间结果放进寄存器中。
实际上,在X86/Linux平台上,专门有一条指令用于++i操作,那就是INC指令。INC指令直接对i进行加1操作,并将结果保存在i中,而不是寄存器中。现在来分析代码1对应的汇编代码片段:
...
pushl %ebp
//老ebp入栈
movl %esp, %ebp
//栈顶指针存入ebp寄存器
subl $4 %esp
//在栈上为局部变量i分配空间
movl $0, 4(%ebp)
//4(%ebp)=i,将i赋初值0
incl -4(%ebp)
//++i,将i加1,此时i为1
incl -4(%ebp)
//++i,将i加1,此时i为2
movl -4(%ebp), %eax //将i放入eax,值为2
movl -4(%ebp), %edx //将i放入edx,值为2
addl %edx, %eax
//(++i) + (++i),将i与i相加,eax值为4
incl -4(%ebp)
//++i,将i加1,此时i为3
addl -4(%ebp), %eax //((++i) +
(++i)) + (++i),即3+4=7
...
代码2的汇编代码片段:
...
pushl %ebp
//老ebp入栈
movl %esp, %ebp
//栈顶指针存入ebp寄存器
subl $4 %esp
//为i分配空间
movl $0, 4(%ebp)
//4(%ebp)=i,将i赋初值0
incl -4(%ebp)
//++i,此时i为1
incl -4(%ebp)
//++i,此时i为2
incl -4(%ebp)
//++i,此时i为3
movl -4(%ebp), %eax //将i值放入eax
movl -4(%ebp), %edx //将i值放入edx
addl %edx, %eax
//edx与eax相加,即((++i)+(++i)),此时eax值为6
movl %eax, %edx
//将eax放入edx
addl -4(%ebp), %edx //将edx与i相加,即(++i)+((++i)+(++i))),即6+3=9
...
相反,在SPARC/Solaris系统上,由于不存在INC指令,那么计算的结果将保存在寄存器中,因此,运算结果为6。
2.C++中:
在C++中,简单类型的i++与++i与C相同。不同的是,由于在C++中可以重载运算符,在实现新的类型对象时重载i++与++i操作时的区别。比如:
class A
{
public:
...
A & operator++();
//++ i
const A
operator++(int);
//i++
...
};
A& A::operator++()
{
*this += 1;
//先加1
return *this;
//返回加1后的值
}
const A A::operator++(int)
{
A oldValue = *this;
//保存旧值
++(*this);
//再加1
return oldValue;
}
从上面的++i与i++的实现看出,在C++中,对与非内建型别来说,前自增运算(++i)返回的是对象的引用,而后自增运算返回的是对象,返回对象将造成拷贝构造函数更多的调用,所以++i的效率要高与i++。
但是,对于内建型别(也就是int,short,char等类型),i++与++i的效率几乎没有区别(因为内建型别没有拷贝构造函数)。与此类似的是i--和--i。
Copyright 2011-2020 © MallocFree. All rights reserved.