#include "stdint.h"
uint8_t while_inc(uint8_t   ch)
{
    uint8_t i = 0;
    do
    {
         ch ++;
    }while (++i < 8);
    return ch;
}
#include "stdint.h"
uint8_t  while_dec(uint8_t ch)
{
    uint8_t i = 0;/*不产生汇编代码*/
    i = 8;
    do
    {
       ch ++;
    } while (--i);
    return ch;
}
代码-1  用“++”判断的while代码段
代码-2  用“--”判断的while代码段

上面两段示例代码中,循环次数都一样,但是在有些平台中,时空效率却大不相同。以51架构单片机为例,我们来分析它编译出来的汇编代码。

             ; FUNCTION _while_inc
;---- Variable 'ch' assigned to Register 'R7' ---- /* 用R7表示形参ch */
;---- Variable 'i' assigned to Register 'R6' ----/*用R6表示局部变量i*/
0000 E4                CLR     A        /* i = 0; */
0001 FE                MOV     R6,A
0002         ?C0003:
0002 0F                INC     R7       /* ch++ */
;  代码“while (++i < 8)”对应汇编语句
0003 0E                INC     R6
0004 EE                MOV    A, R6
0005 C3                CLR     C
0006 9408              SUBB   A, #08H
0008 40F8              JC      ?C0003
000A         ?C0004:
000A 22                RET     
代码-3  C51平台++”编译得到的汇编代码段(C51平台)
             ; FUNCTION _while_dec
;---- /* 用R7表示形参ch   用R6表示局部变量i */
0000 E4                CLR     A         /* uint8_t i = 0 令人困惑?? */
0001 7E08              MOV     R6,#08H  /* i = 8; */
0003         ?C0003:
0003 0F                INC     R7       /* ch++ */
;  代码“while (--i)”对应汇编语句
0004 DEFD              DJNZ    R6,?C0003
0006         ?C0004:
0006 22                RET     
代码-4  C51平台“--”编译得到的汇编代码段(C51平台)

从上述的汇编代码中,我们发现用“++”和“--”的两段while循环代码,在空间上前者比后者多了4条指令共5个字节,时间上多花费了8次*4=32个指令周期如果循环次数进一步加大,这一差距就不能忽视。

为何会有如此之多的差异?其本质原因是因为C51拥有一条“复合指令DJNZ”,其定义如下,意为:将“寄存器或者某个ram内的byte变量”的值减1,完事后判断不为0则跳转”。该指令融合了“算术运算 + 逻辑运算 + 跳转”三个功能。


所以,你该知道,大牛的代码while用"--和0比较",一般的代码就是“++和var比较”或者用“for循环”。
一般来说,不管什么平台,判断0总是效率较高的,毕竟cpu的状态寄存器都有个Zero状态位。


------------------------------------------------------------------------------------------------------------------------------------------------

而对于M3平台,两段代码并没有带来明显的时空差异。

while_inc PROC
        MOVS     r1,#0
|L1.2|
        ADDS     r0,r0,#1
        ADDS     r1,r1,#1
        UXTB     r1,r1
        UXTB     r0,r0
        CMP      r1,#8
        BCC      |L1.2|
        BX       lr
        ENDP
while_dec PROC
        MOVS     r1,#8
|L1.2|
        ADDS     r0,r0,#1
        SUBS     r1,r1,#1
        UXTB     r0,r0
        ANDS     r1,r1,#0xff
        BNE      |L1.2|
        BX       lr
        ENDP
M3平台“++”的汇编代码段(M3平台)
M3平台“--”的汇编代码段(M3平台)

这两段汇编,前者之所以比后者多了一句“UXTB     r1,r1”,因为加法的值需要32位和8位的数值转换,归零的减法不需要。这又应那个原则:“零值”操作效率较高。

因此,一个软件工程师,需要对其操作平台的指令系统有足够的熟悉,对基本C语句的汇编转换了然于心,才能写出高质、高效的代码。老工程师的价值,也体现在此。

另外值得一提的是,在这个实验中我发现一个比较有意思的现象:在函数while_dec中变量声明兼初始化代码“uint8_t i = 0;”,紧随其后就是“i = 8”的覆盖赋值代码。而它在C51平台编译器上,却产生了一个冗余代码“CLR   A”,令人困惑。在M3平台的编译中,就没有这样的现象了。不管怎样,随着编译器越来越智能,变量声明时即刻初始化,是一个非常好的编程习惯,它不会对代码空间增加额外的负担。