#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_twhile_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
代码-3C51平台“++”编译得到的汇编代码段(C51平台)
; FUNCTION _while_dec
;---- /*用R7表示形参ch 用R6表示局部变量i */
0000 E4 CLR A /* uint8_t i = 0令人困惑?? */
00017E08 MOV R6,#08H/* i = 8; */
0003 ?C0003:
0003 0F INC R7 /* ch++ */
;代码“while (--i)”对应汇编语句
0004 DEFD DJNZ R6,?C0003
0006 ?C0004:
0006 22 RET
代码-4C51平台“--”编译得到的汇编代码段(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平台的编译中,就没有这样的现象了。不管怎样,随着编译器越来越智能,变量声明时即刻初始化,是一个非常好的编程习惯,它不会对代码空间增加额外的负担。