LPC 进阶－３　最佳化程式码

一、佳乎？不佳乎？

  在讨论最佳化程式码的时候，有个词需要先给它定义一下：什么是好的程式
码？相信这个问题的答案人人不同，但相信异中有同：

  一段好的程式码是要能得到正确答案外，还能容易修改，必要时还要不同人
也能容易阅读与修改，当然，还要尽量不占用系统负载。

  我们得承认，我觉得是好的程式码，也许别人不这么想，尽管如此，这里还
是有一些标准可供参考，希望能帮助读者早日步入资深程式设计师的行列。

二、风格

  虽然程式是由程式设计师写出来的，但是我们一直要强调一件事，程式不是
只有设计者会去读、修改，尤其在泥巴中。因此，与其讨论风格不如早点讨论
最佳化程式码本身不是吗？事实上正因为在泥巴中程式码是共享的，风格才会
显得更重要，此处的风格不再只是程式者本身的，而是程式码的风格。

  有人说资深程式师会用 %20  的时间写出 80%  的程式码来，剩下 80%  的
时间则写那 20%  的部份与除错。这似乎有点轻视程式师的能力，不过，当我
们看普通程式师如何写程式会发现：他们用 80%  的时间只写了 20%，而且那
20% 的程式让他们发现里头一大堆错误，所以他们回过头来又花了 80%  的时
间除错，到最后甚至产生不了一个正确的程式码来。

  要想用 20%  的时间很快的写出正确而优良的 80%  的程式码，最重要的事
就是建立程式风格。不啰嗦，底下举例，请读者们先别急着了解程式码：

  for(i=0;i<j;i++){printf("the value is %d\n",i);
  }printf("and, the max value is %d\n",j);

  让我们比较下面一样功能的码：

  for (value = 0; value < MAX; value++)
  {
    printf ("the value is %d\n");
  }
  printf ("and, the max value is %d\n", MAX);

  我们已经用最简单的例子来说明风格，不良风格的码不但阅读不易，除错困
难外，往后要维修也是很困难。不过，所谓风格就是特色，是没有一定的标准
可言，不过仍然有规则可寻：

  １、缩排

　　上例就是一个好例子，第一个 printf() 是 for  回圈内的叙述，因此缩
排进去，而第二个 printf() 则地位跟 for  相同，因此栏位相同。不过，缩
排多少空白倒没有一定结论，有人喜欢用定位字元（Tab）， 有人缩两格，有
人缩三格，我想这就是所谓的风格吧。

    什么时候要缩排？我想上例也显示了原则：以叙述所占的地位缩排。通常
是 if/else，while，switch，for  等等，底下再举例，先看没适当缩排得：
  
    if (a > b) {
    if (c > d) {
    printf ("c > d\n");
    }
    else {
    printf ("c <= d\n");
    }
    printf ("a > b\n");
    }
    else {
    printf ("a <= b\n");
    }

    底下是有缩排的：
  
    if (a > b) {
        if (c > d) {
            printf ("c > d\n");
        }
        else {
            printf ("c <= d\n");
        }
        printf ("a > b\n");
    }
    else {
        printf ("a <= b\n");
    }
  
    相信大家能同后者较容易阅读吧！

  ２、配对括号

　　让我们再看一段跟同上一例的不同风格：

    if (a > b)
    {
        if (c > d)
	{
            printf ("c > d\n");
        }
        else
	{
            printf ("c <= d\n");
        }
        printf ("a > b\n");
    }
    else
    {
        printf ("a <= b\n");
    }
  
    这两种风格谁好谁坏我们不给定论，由读者自行选择。
    不过我们再看另一例子：

    if (((a>b) && (c>d)) || ((a>c) && (b>d))) win();

    与：

    if ( ( (a > b) && (c > d) ) || ( (a > c) && (b > d) ) )
    {
      win();
    }

    与：

    if ( ((a>b) && (c>d)) || ((a>c) && (b>d)) )
      win();

    那一个较容易阅读？我是比较倾向于第三者，这背后有作者个人的理由，
事实上也是风格的特色。我的想法如下：
    &&  运算子是要同时成立的叙述，因此可以看成同一个叙述，我就尽量将之
放在一起，而 || 运算子可看成不同叙述，所以就放开一点。而 win()  已经不
是 if 叙述的一部份，而且是附在 if 下的，所以换行且缩排。另外，之所以不
选第二种状况给予一对大括号是因为， if 下的叙述只有 win(); 缩排已经可以
突显出来。或许有人喜欢下列第四种状况：

    if ( ((a>b) && (c>d)) || ((a>c) && (b>d)) )
    {
      win();
    }

    当然，这得由读者自行选择。我们看看多行的例子：

    if (x > y) {
      z = x + y;
      y += 2;
    }

    跟

    if (x > y)
    {
      z = x + y;
      y += 2;
    }

    此例中，也许出乎意料，我是选前者，没必要左右大括号要同一栏，前者
一样可以由右大括号知道 if 叙述已经结束，且可以省一行。

  ３、白空

    for(i=0;i<100;i+=2)printf("%d\n",i);

    这是印出 0 到 100 间的偶数的程式码，跟下面的码比较看看：

    for ( i=0; i<100; i+=2 )
      printf ("%d\n", i);

    及

    for ( i = 0; i < 100; i += 2)
      printf ( "%d\n", i );

    第一种空白太少，而第三种又太多，这是一种感觉，由读者自行揣摸。
    适当的空白可以增加可读性，立即效果就是不易伤脑筋，自然写得快啰。

    白空不止是空白，还包括定位字元（Tab）、 换行，我们希望能多利用白
空提升程式的可读性，也就是塑造程式风格。

  ４、命名

　　变数与函数的名称如果太简单，容易丧失可读性，日后可能无从了解程式
的意义，我们举例来看：

    int i,j;
    string k="Hello, world.\n";

    j = strlen (k);
    for (i=0; i<j; i++)
      if (k[i] <= 123 && k[i] >= 98)
        k[i] = k[i] - 32;

    上一段会把小写转成大写，让我们比较下面一段相同功能的码：

    int		i, len;
    string	msg = "Hello, world.\n";

    len = strlen (msg);
    for (i=0; i<len; i++)
      if (msg[i] <= 'z' && msg[i] >= 'a')
        msg[i] = msg[i] - ('a' - 'A');

  ５、注解

　　大部份的人都会认为注解太麻烦，与而且还不急嘛！有这种想法的人大部
份都很少真正用心在写注解。虽然把注解摆在最后，并不是因为不重要，而是
希望读者能记得更牢靠。事实上，注解不止是风格的一部份，说严重点，它可
说是与程式师的德性有关。勤于写注解的人一般来说，较注重完美，与重视他
人的感受。不过，注解太多有可能造成阅读者的困扰，这一点我们无法提供标
准，只能说，在适当的地方写注解是程式师的责任。

    底下有一些参考：

    档头：包含程式目的、语法（用法）、原创者（沿革）、日期、资料型态
　　函数原型：应宣告所有函数前面，通常是在整体变数后面。
