i) 嵌套的if语句:
if (STATUS = = A) React_A();
else if (STATUS = = B) React_B();
else if ....
ii) switch-case语句:
switch (STATUS)
case (A): React_A(); break;
case (B): React_B(); break;
...
iii) 函数指针: (假定状态A, B, ... 是顺序编号的值,或是枚举类型值)
void React_Func[] = {React_A, React_B, ...};
...
React_Func[STATUS]();
具体采用哪种方式,依据反复次数而定。表2给出了不同方法对ROM和RAM空间的占用情况。从中可看出“switch”方式的可读性最强,但在反复次数少(函数个数少)的情况下,占用的空间最大。
“if”方式的可读性较好,占用的空间也比较小。而“pointer”方式占用ROM的空间相对变化不大,但占用许多RAM空间。
存储模式和零页的使用
不同的MCU有不同的存储模式。在CodeWarrior for HC(S)08 (V3.1)中,建立工程的时候有small和tiny两种模式可供选择:SMALL模式,如果没有特殊的说明,所有的指针和函数地址都被假定为16位的地址,此模式中代码和数据都被存储在64k的地址空间内;TINY 模式,所有的数据包括堆栈都分配在零页地址空间内,如果没用关键字_far作特殊说明,所有数据指针都被假定为8位地址,但是代码的地址空间仍然是64k,函数指针也仍是16位的长度。
前面讨论中说过,变量放在零页地址内生成的代码较少,而且能有效的支持位运算。在HC(S)08系列单片机中,外围寄存器一般占用$00-$3F的地址空间,所以留给RAM的零页地址空间是有限的。为了缩减生成的代码,就要把频繁使用的变量放在零页内。要根据子程序、函数参数和局部变量使用的情况,确定堆栈的使用频率,如果频率高就把堆栈放置在零页地址内。减少生成的代码,我们也要减少子程序中的参数(因为要用到A和HX寄存器),把经常使用的临时变量定义成全局变量放在零页地址中。当然,全局变量是共享的,所以用的时候我们要格外小心。下面的例程中,在Calc()函数中,可以改变全局变量gTemp2和gTemp3的值,但不能改变变量gTemp1的值,因为一开始就对子程序进行了这个设定。通常,好的变量名可以帮我们清楚的区分变量的作用范围。比如分别以1、2、3结尾的变量,可以设定等级1的子程序只能用1结尾的变量,等级2的子程序只能用2、3结尾的变量。
uint8_t gTemp1, gTemp2, gTemp3; // 存放临时数据的全局变量,所有函数都可以访问
void_t Calc( uint8_t in) {
gTemp3 = 0 ;
for (gTemp2 = 5 ; gTemp2 !=0 ; gTemp2--) gTemp3 += ADCR * t_in;
}
void main( ) {
...
for (gTemp1 = 0; gTemp1 < 3; gTemp1++)
Calc(array[gTemp1]) ;
...
}
初始化的优化
在CodeWarrior中,每个工程都有一个模板,Start-up启动函数已经预先写好,我们可以在建工程的时候选择是否采用ANSI标准初始化程序。通常,标准初始化程序的代码效率并不高(可以参看start08.c文件中的源程序)。为了减少生成的代码,我们应该采用非ANSI标准的初始化程序,由用户自行编写。比如,仅做堆栈指针初始化、RAM清空和跳转到main函数三项工作,用如下汇编代码实现。
asm {
clra ; 得到清零数据
ldhx #MAP_RAM_last ; 指向RAM的尾部
stx MAP_RAM_first ; 使得RAM起始地址内的数值非零
txs ; 初始化堆栈指针
ClearRAM:
psha ; 清空当前RAM地址
tst MAP_RAM_first ; 检测是否完成RAM的清空
bne ClearRAM ; 没有完成就继续
txs ; 初始化堆栈指针
jmp main ; 跳转到main()函数
}
除了这些通用的起始程序,还需要对硬件和变量进行初始化。尽管寄存器都有默认值,但仍要培养用软件对硬件初始化的好习惯。对于变量,最好初始值为零,因为清空RAM代码已经完成了这个工作。为了防止代码臃肿,建议把相同初始值的变量归为一组,这样可以用循环的方式对它们进行初始化。在优化代码的时候,要特别注意那些可变型volatile变量(比如寄存器),因为编译器是不会对这些变量进行优化的。
结语
本文简述了一些优化代码的方法,包括变量的选择、使用静态类型、数组和指针的挑选、如何使用存储模式和如何进行初始化等。但是,这仅是所有方法的一部分。一个高效的C语言程序,不仅要代码少、执行速度快,而且要清楚、简洁、准确和易注释。此外,程序要有一个好的架构,便于移植和维护。代码的再使用性(reuse)也是一个关键因素,这不在于代码本身,而在于它能减少开发调试时间。所以说,高效的C语言程序是各种因素的综合体,需要我们全面考量。 |