针对刚接触编程的新手常常容易忽视的一些细节而又关键的问题,想要让你的智能车控制代码更规范、更高效、更节省时间,岱默给出如下建议:
1、使用尽量小的数据类型能够使用字符型(char)定义的变量,就不要使用整型(int)变量来定义;能够使用整型变量定义的变量就不要用长整型(longint),这样做能有效节省数据空间,在定义变量后不要超过变量的作用范围,如果超过变量的范围赋值,C编译器并不报错,但程序运行结果却错了,而且这样的错误很难发现。
2、在设计较复杂算法(比如图像处理算法)时,能不使用浮点型(float)变量就不要使用浮点型变量,尽量转换为整形变量,运算速度可以提高甚至几十倍。在控制周期为毫秒级的智能车中运算时间尤为重要。
3、用移位实现乘除法运算 a=a*4;b=b/4;可以改为:a=a<<2;b=b>>2;常如果需要乘以或除以2n,都可以用移位的方法代替。用移位的方法得到代码比调用乘除法子程序生成的代码效率高。实际上,只要是乘以或除以一个整数,均可以用移位的方法得到结果,如:a=a*9可以改为:a=(a<<3)+a采用运算量更小的表达式替换原来的表达式,下面是一个经典例子:
旧代码:
x=w%8;y=pow(x,2.0);z=y*33; for(i=0;i<MAX;i++)
{
h=14*i;printf("%d",h);
}
新代码:
x=w&7;/*位操作比求余运算快*/
y=x*x;/*乘法比平方运算快*/
z=(y<<5)+y;/*位移乘法比乘法快*/
for(i=h=0;i<MAX;i++)
{
h+=14;/*加法比乘法快*/
printf("%d",h);
}
4、避免不必要的整数除法,整数除法是整数运算中最慢的,所以应该尽可能避免。一种可能减少整数除法的地方是连除,这里除法可以由乘法代替。这个替换的副作用是有可能在算乘积时会溢出,所以只能在一定范围的除法中使用。
不好的代码:
Int i,j,k,m;
m=i/j/k;
推荐的代码:
inti,j,k,m;
m=i/(j*k);
5、使用增量和减量操作符在使用到加一和减一操作时尽量使用增量和减量操作符,因为增量符语句比赋值语句更快,原因在于对大多数CPU来说,对内存字的增、减量操作不必明显地使用取内存和写内存的指令,比如下面这条语句:x=x+1;模仿大多数微机汇编语言为例,产生的代码类似于:moveA,x;把x从内存取出存入累加器 AaddA,1;累加器A加1storex;把新值存回x如果使用增量操作符,生成的代码如下:incrx;x加1显然,不用取指令和存指令,增、减量操作执行的速度加快,同时长度也缩短了。
6、循环优化
(1)、充分分解小的循环
要充分利用CPU的指令缓存,就要充分分解小的循环。特别是当循环体本身很小的时候,分解循环可以提高性能。注意:很多编译器并不能自动分解循环。 不好的代码:
// 3D转化:把矢量 V 和 4x4 矩阵 M 相乘
for (i = 0; i < 4; i ++)
{
r[i] = 0;
for (j = 0; j < 4; j ++)
{
r[i] +=M[j][i]*V[j];
}
}
推荐的代码:
r[0] =M[0][0]*V[0] + M[1][0]*V[1] + M[2][0]*V[2] + M[3][0]*V[3];
r[1] =M[0][1]*V[0] + M[1][1]*V[1] + M[2][1]*V[2] + M[3][1]*V[3];
r[2] =M[0][2]*V[0] + M[1][2]*V[1] + M[2][2]*V[2] + M[3][2]*V[3];
r[3] =M[0][3]*V[0] + M[1][3]*V[1] + M[2][3]*V[2] + M[3][3]*v[3];
(2)、提取公共部分对于一些不需要循环变量参加运算的任务可以把它们放到循环外面,这里的任务包括表达式、函数的调用、指针运算、数组访问等,应该将没有必要执行多次的操作部集合在一起,放到一个 init的初始化程序中进行。
(3)、延时函数
通常使用的延时函数均采用自加的形式:
void delay(void)
{
unsignedint i;
for(i=0;i<1000;i++) ;
}
将其改为自减延时函数:
void delay(void)
{
unsignedint i;
for(i=1000;i>0;i--) ;
}
两个函数的延时效果相似,但几乎所有的 C编译对后一种函数生成的代码均比前一种代码少1~3个字节,因为几乎所有的 MCU均有为0转移的指令,采用后一种方式能够生成这类指令。在使用while循环时也一样,使用自减指令控制循环会比使用自加指令控制循环生成的代码更少1~3个字母。但是在循环中有通过循环变量“i”读写数组的指令时,使用预减循环有可能使数组超界,要引起注意。
(4)、while循环和do…while循环
用while循环时有以下两种循环形式:
unsignedint i;
i=0;
while(i<1000)
{
i++;
//用户程序
}
或:
unsignedint i;
i=1000;
do
{
i--;
//用户程序
}
while(i>0);
在这两种循环中,使用 do…while循环编译后生成的代码的长度短于 while循环。
(5)选择好的无限循环
在编程中,我们常常需要用到无限循环,常用的两种方法是 while (1) 和 for (;;)。
这两种方法效果完全一样,但那一种更好呢?然我们看看它们编译后的代码:
编译前:
while (1);
编译后:
mov eax,1
test eax,eax
je foo+23h
jmp foo+18h
编译前:
for (;;);
编译后:
jmp foo+23h
显然,for (;;)指令少,不占用寄存器,而且没有判断、跳转,比 while (1)好。
7、把本地函数声明为静态的(static)如果一个函数只在实现它的文件中被使用,把它声明为静态的(static)以强制使用内部连接。否则,默认的情况下会把函数定义为外部连接。这样可能会影响某些编译器的优化——比如,自动内联。
8、使用嵌套的 if结构在if结构中如果要判断的并列条件较多,最好将它们拆分成多个 if结构,然后嵌套在一起,这样可以避免无谓的判断。
9、Switch语句中根据发生频率来进行 case排序Switch 可能转化成多种不同算法的代码。其中最常见的是跳转表和比较链/树。当switch用比较链的方式转化时,编译器会产生 if-else-if的嵌套代码,并按照顺序进行比较,匹配时就跳转到满足条件的语句执行。所以可以对 case的值依照发生的可能性进行排序,把最有可能的放在第一位,这样可以提高性能。此外,在case中推荐使用小的连续的整数,因为在这种情况下,所有的编译器都可以把 switch 转化成跳转表。不好的代码:
intdays_in_month, short_months,normal_months, long_months;
switch(days_in_month)
{
case 28:
case 29:
short_months++;
break;
case 30:
normal_months++;
break;
case 31:
long_months++;
break;
default:
cout<< "month has fewer than 28 or more than 31 days" << endl;
break;
}
推荐的代码:
intdays_in_month, short_months,normal_months, long_months;
switch(days_in_month)
{
case 31:
long_months++;
break;
case 30:
normal_months++;
break;
case 28:
case 29:
short_months++;
break;
default:
cout<< "month has fewer than 28 or more than 31 days" << endl;
break;
}
当然,以上只是岱默总结出来的针对新手的一些最常见但是很有效的优化方案,肯定还有其他好的方法是岱默所遗漏的,想要在程序猿的路上越走越远,一定还有更多更多的知识等待着大家去发掘,岱默与你共同进步!
联系我们
淘宝店铺 : http://shop60443799.taobao.com/
技术交流QQ群 : 108190422 摄像头群
132879827 光电群
118404899 电磁群
技术交流邮箱 : demok@vip.qq.com
技术论坛 : http://blog.csdn.net/demok2010
官方网站 : www.demok.com.cn
//--------------------------------------------END-----------------------------------------------------//