(1)引用变量
C++中的指针和引用就像两兄弟,他们几乎有着相同的功能,在大多数情况下都可以互相替换。指针可用CPU间接寻址来实现,其执行效率会比寄存器寻址要慢。引用常被说成是一种别名,可是CPU指令系统里并没有引用和别名这类东西,那么它是如何实现的呢?它会比指针更快吗?为搞明白这个问题,我做了一次试验。
对下面这点代码:
void test1(int *i)
{
(*i) ++;
}
void test2(int &i)
{
i ++;
}
先用gcc编译,然后用objdump反编译
$ gcc -O2 -c pointer_and_reference.cpp
$ objdump -d pointer_and_reference.o
得到:
00000000 <_Z5test1Pi>:
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 8b 45 08 mov 0x8(%ebp),%eax
6: 83 00 01 addl $0x1,(%eax)
; i++
9: 5d pop %ebp
a: c3 ret
b: 90 nop ; 我没有看懂
这两句干什么用的
c: 8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi
00000010 <_Z5test2Ri>:
10: 55 push %ebp
11: 89 e5 mov %esp,%ebp
13: 8b 45 08 mov 0x8(%ebp),%eax
16: 83 00 01 addl $0x1,(%eax)
; i++
19: 5d pop %ebp
1a: c3 ret
除了我没有看懂的两行代码(在ret之后的奇怪指令),其余代码完全一样。可见引用其实就是用指针实现,两者在效率上并没有差别。
(2)循环优化
C语言写循环还是优点麻烦,通常是会这样写:
int i;
for (i = 0; i < 100; i ++)
{
......
}
其实这个循环很简单,就是要里面的语句循环100遍,i=0、i<100、i++这三条语句显得有些累赘。不知道GCC能否明白我的意图,用最高效的方式实现循环呢?
对下面这段程序:
int test_for()
{
int i;
int j=0;
for (i = 0; i < 100; i ++)
j += i + 1;
return j;
}
函数的作用就是返回从1加到100的结果,小学生都知道结果是5050。若不启用编译优化,其编译结果是:
00000000 <test_for>:
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 83 ec 10 sub $0x10,%esp
6: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%ebp) ;j=0
d: c7 45 f8 00 00 00 00 movl $0x0,-0x8(%ebp) ;i=0
14: eb 0d jmp 23 <test_for+0x23> ;跳到23
16: 8b 45 f8 mov -0x8(%ebp),%eax ;j = i + 1
19: 83 c0 01 add $0x1,%eax
1c: 01 45 fc add %eax,-0x4(%ebp)
1f: 83 45 f8 01 addl $0x1,-0x8(%ebp) ;i ++
23: 83 7d f8 63 cmpl $0x63,-0x8(%ebp) ;i < 100就跳到16
27: 7e ed jle 16 <test_for+0x16>
29: 8b 45 fc mov -0x4(%ebp),%eax ;返回值=j
2c: c9 leave
2d: c3 ret
这几乎就是把上面源程序组条编译的结果,没有任何优化。没关系,用编译优化再试一次(加-O2参数),得到结果:
00000000 <test_for>:
0: 55 push %ebp
1: b8 ba 13 00 00 mov $0x13ba,%eax ;返回值=5050
6: 89 e5 mov %esp,%ebp
8: 5d pop %ebp
9: c3 ret
a: 8d b6 00 00 00 00 lea 0x0(%esi),%esi ;这句再次没看懂
不得了了!GCC不需要生成循环代码,直接把正确结果编译出来了!实在是太神奇了!
可是上面几乎是最标准的循环写法,在实际编程中,我们常常写一些不标准的循环语句,GCC能否有一样的优化结果呢?
编译下面这段程序:
int test_for2()
{
int i=0;
int j=0;
for (; i < 100; j += i + 1, i ++);
return j;
}
int test_while()
{
int i=0;
int j=0;
while (i < 100) {
j += i + 1;
i ++;
}
return j;
}
int test_while2()
{
int i=0;
int j=0;
while (1) {
j += i + 1;
if (++ i >= 100)
break;
}
return j;
}
每个函数编译得到的结果和上一次得到的完全一样,在这里就不打印出来了。此刻,我心里对GCC佩服得五体投地。