GCC 编译试验:引用变量和循环优化

(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佩服得五体投地。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值