使用临时中间变量,会降低C语言程序效率吗?

在C语言程序开发中,有时为了一行代码不至于过长,或者其他原因,程序员常常会使用一些临时的中间变量,使用临时中间变量的C语言代码,大都可以抽象成类似于下面这样的例子,请看:

int i = 5;
int j = 10;
int result = i+j;

到这里,有些小伙伴就有疑问了,使用临时中间变量会降低C语言程序的效率吗?或者说,将上面的 3 行代码写成一行:

int result = 5+10;

是否能够得到更高效率的C语言程序呢?鉴于使用临时中间变量常常能够提升代码的可读性,要是它同时真的会降低程序效率,我们就陷入矛盾了。

究竟有没有必要使用长表达式,代替临时中间变量呢?

其实,绝大多数现代C语言编译器已经足够聪明,优化上述代码轻而易举。我们还是以上述C语言代码为例,对其稍作调整,相关代码如下,请看:

#include <stdio.h>

void func()
{
  int i = 5;
  int j = 10;
  int result = i + j;

  printf( "%d\n", result ) ;
}

file
以 gcc 编译器为例,添加 -std=c99 指定 C99 标准,并指定 -O3 优化项,应该能够得到上述C语言代码对应的汇编代码:

movl    $15, %esi

C语言编译器常用的一个策略“constant propagation(常数传播,下文讨论)”会将 i+j 在编译阶段计算完毕。

另外值得说明的是,我在上述C语言代码中添加了 printf() 函数读取 result,否则 fun() 函数将直接被优化成下面这样的汇编代码:

func:
  rep ret

机器甚至都不会再去计算和保存 i+j,直接就返回了。C语言编译器做出这样的优化,显然可以避免无效代码消耗机器性能,因为 fun() 函数中并无实际的代码要用到 result,再去计算和处理它,显然就是做无用功。

当然了,若是考虑到硬件寄存器,即使没有代码使用到result,对其赋值也可能是有意义的。这是就需要一些其他操作,来避免编译器优化了。这一点,我之前的文章讨论过,感到陌生的读者可以翻翻看看。

可见,即使在C语言代码中使用了一些临时的中间变量,也不用太过担心它们会影响效率,因为这对于编译器来说,优化它们轻而易举。

编译器的“常数传播”和“常数折叠”策略

考虑到一些初学者不太熟悉编译器的策略,这里对前文设计的“常数传播”策略稍作解释。常数折叠(Constant folding)以及常数传播(constant propagation)都是C语言编译器对代码做优化使用的策略之一。

“常数折叠”是在编译期间简化常数的过程。常数在C语言程序中仅仅代表一个简单的数值,事实上,若某个变量从未被修改,完全可以将其也当作常数,请看下面这个例子:

 i = 320 * 200 * 32;

多数现代C语言编译器不会真的产生两个乘法指令,然后再将结果存储起来,而是辨识是否之后没有代码修改变量 i,若如此,则会在编译阶段就将结果计算出来,之后直接使用计算结果。此即一次典型的“常数折叠”过程,这样做显然可以减小C语言代码尺寸,并且能够得到更高效的程序。

“常数传播”则是一个替代表达式中已知常数的过程,也是在编译时执行的,这里仍然以实例讨论,请看下面这段C语言代码:

  int x = 14;
  int y = 7 - x / 2;
  return y * (28 / x + 2);

编译器执行“常数传播”策略,则 x 会被替换为常数 14,得到下面这样的C语言代码:

  int x = 14;
  int y = 7 - 14 / 2;
  return y * (28 / 14 + 2);

继续这一传播过程,则最终得到下面这样的C语言代码:

 int x = 14;
  int y = 0;
  return 0;

编译器通常还会执行一些消除无用代码的过程,以对整个C语言程序优化,因此在现代编译器看来:

  int x = 14;
  int y = 7 - x / 2;
  return y * (28 / x + 2);

其实和简单的 return 0; 没什么两样。

小结

本文在最后介绍了现代C语言编译器常用的“常数折叠”和“常数传播”策略,可见,如今的编译器早已不再是只会逐行“死板”的处理代码了,因此,我们在编写C语言代码时,除非在某些特殊的情况下,否则更多的应该关心代码的可读性。那些复杂的“优化”工作,交给编译器去做就好了。

https://zh.wikipedia.org/wiki/%E5%B8%B8%E6%95%B8%E6%8A%98%E7%96%8A

阅读更多:   C语言
添加新评论

icon_redface.gificon_idea.gificon_cool.gif2016kuk.gificon_mrgreen.gif2016shuai.gif2016tp.gif2016db.gif2016ch.gificon_razz.gif2016zj.gificon_sad.gificon_cry.gif2016zhh.gificon_question.gif2016jk.gif2016bs.gificon_lol.gif2016qiao.gificon_surprised.gif2016fendou.gif2016ll.gif