绚紫飞鸥的个人空间 https://bbs.21ic.com/?1185562 [收藏] [复制] [RSS]

日志

(摘)编写代码时的优化

已有 520 次阅读2014-12-27 21:17 |系统分类:单片机

1、尽可能选择无符号数且不使用小数
使用Keil  C编程时,不论是char型还是int型,要尽可能采用unsigned型的数据,因为在处理有符号
数时,程序要对符号进行判断和处理,运算速度会减慢,浮点小数float对程序运行速度影响更大,所以在
单片机中用得最多的数据类型还是unsigned char 、unsigned int、unsigned long   3种类型。
2、选择合适的算法和数据结构
数组与指针语句具有十分密码的关系,一般来说,指针比较灵活简洁,而数组则比较直观,容易理解。对
于大部分的编译器,使用指针比使用数组生成的代码更短, 执行效率更高,但是在Keil中则相反,使用数
组比使用指针生成的代码更短。
3、使用尽量小的数据类型
能够使用字符型(char)定义的变量,就不要使用整型(int)变量来定义,能够使用整型变量定义的变量就不要
用长整型(long int),能不使用浮点型(float)变量就不要使用浮点型变量。当然,在定义变量后不要超过变量
的作用范围,如果超过变量的范围赋值,C编译器并不报错,但程序运行结果却错了,而且这样的错误很
难发现。
4、使用自加、自减指令
通常使用自加、自减指令和复合赋值表达式(如 a-=1及 a+=1等)都能够生成高质量的程序代码,编译器通常
都能够生成 inc和 dec之类的指令,而使用 a=a+1或 a=a-1之类的指令,有很多 C编译器都会生成二到三个
字节的指令。
5、减少运算的强度
可以使用运算量小但功能相同的表达式替换原来复杂的的表达式。如下:
(1)、求余运算。
a=a%8; 可以改为: a=a&7; 说明:位操作只需一个指令周期即可完成,而大部分的 C编译器的“%”运算
均是调用子程序来完成,代码长、执行速度慢。通常,只要是求 2n方的余数,均可使用位操作的方法来代替。
(2)、平方运算
a=pow(a,2.0); 可以改为:a=a*a;
说明:在有内置硬件乘法器的单片机中(如 51系列),乘法运算比求平方运算快得多,因为浮点数的求平方是
通过调用子程序来实现的.如果是求 3次方,如: a=pow(a,3.0); 更改为: a=a*a*a; 则效率的改善更明显。
(3)、用移位实现乘除法运算
a=a*4; b=b/4; 可以改为: a=a<<2; b=b>>2;
说明:通常如果需要乘以或除以 2n,都可以用移位的方法代替,如果乘以 2n,都可以用左移的代码,只要是乘
以或除以一个整数,均可以用移位的方法得到结果,如: a=a*9可以改为: a=(a<<3)+a
6、循环
(1)、循环语
                                                                                                    - 89 -
对于一些不需要循环变量参加运算的任务可以把它们放到循环外面,这里的任务包括表达式、函数的调用、
指针运算、数组访问等,应该将没有必要执行多次的操作全部集合在一起,放到一个 init的初始化程序中进
行。
(2)、延时函数:
通常使用的延时函数均采用自加的形式:
void delay (void)
{
unsigned int i;
for (i=0;i<1000;i++)
}
将其改为自减延时函数:
void delay (void)
{
unsigned int i;
for (i=1000;i>0;i--)
}
两个函数的延时效果相似,但几乎所有的 C编译对后一种函数生成的代码均比前一种代码少 1~3个字节,因
为几乎所有的 MCU均有为 0转移的指令,采用后一种方式能够生成这类指令。在使用 while循环时也一样,
使用自减指令控制循环会比使用自加指令控制循环生成的代码更少 1~3个字节。但是在循环中有通过循环
变量“i”读写数组的指令时,使用预减循环时有可能使数组超界,要引起注意。
(3)while循环和 do…while循环
用 while循环时有以下两种循环形式:
unsigned int i;
i=0;
while (i<1000)
{
i++;
//用户程序
}
或:
unsigned int i;
i=1000;
do
{
        i--;
        // 用户程序
}
while (i>0);
在这两种循环中,使用 do…while循环编译后生成的代码的长度短于 while循环。
7、查表
在单片机程序中一般不进行非常复杂的运算,如浮点数的乘除及开方等,以及一些复杂的数学模型的插补运
算,对这些即消耗时间又消费资源的运算,应尽量使用查表的方式,并且将数据表置于程序存储区。如果直接
生成所需的表比较困难, 也尽量在启动时先计算,然后在数据存储器中生成所需的表,以后在程序运行中直
接查表就可以了, 减少了程序执行过程中重复计算的工作量。
8、合理的使用存储类型
(1)data区空间小,所以只有频繁用到或对运算速度要求很高的变量才放到 data区内,比如 for循环中
的计数值。
(2)data区内最好放局部变量。
因为局部变量的空间是可以覆盖的(某个函数的局部变量空间在退出该函数是就释放,由别的函数的
局部变量覆盖),可以提高内存利用率。当然静态局部变量除外,其内存使用方式与全局变量相同;
(3)保你的程序中没有未调用的函数。
在 Keil C里遇到未调用函数,编译器就将其认为可能是中断函数。函数里用的局部变量的空间是不
释放的,也就是同全局变量一样处理。这一点 Keil C做得很愚蠢,但也没办法。
(4)程序中遇到的逻辑标志变量可以定义到 bdata中,可以大大降低内存占用空间。
在 51系列芯片中有 16个字节位寻址区 bdata,其中可以定义 8*16=128个逻辑变量。定义方法是:
bdata bit LedState;但位类型不能用在数组和结构体中。
(5)其它不频繁用到和对运算速度要求不高的变量都放到 xdata区。
(6)如果想节省 data空间就必须用 large模式,将未定义内存位置的变量全放到 xdata区。当然最好对
所有变量都要指定内存类型。
(7)当使用到指针时,要指定指针指向的内存类型。
在 C51中未定义指向内存类型的通用指针占用 3个字节;而指定指向 data区的指针只占 1个字节;指
定指向 xdata区的指针占 2个字节。如指针 p是指向 data区,则应定义为: char data *p;。还可指
定指针本身的存放内存类型,如:char data * xdata p;。其含义是指针 p指向 data区变量,而其本
身存放在 xdata区。
9、其它
若用到外部数据储存区,应把访问频率不高的全局变量放到外部数据储存区,访问频率很高的局部变量放
到内部 RAM,函数退出时局部变量立即释放掉了。
比如使用在线汇编及将字符串和一些常量保存在程序存储器中,均有利于优化,对 idata 的定义的变量最
好放在 data 变量之后
对于这一种定义
uchar c1;
idata uchar c2;
uchar c3;
变量 c2 肯定会以间接寻址,但它有可能落在 data 区域,就浪费了一个可直接寻址的空间

路过

鸡蛋

鲜花

握手

雷人

评论 (0 个评论)