适当的使用宏
在 C程序中使用宏代码可以提高程序的执行效率。宏代码本身不是函数,但使用起来像函数。函数调用要使用系统的栈来保存数据,同时 CPU 在函数调用时需要保存和恢复当前的现场,进行进栈和出栈操作,所以函数调用也需要 CPU时间。而宏定义就没有这个问题:宏定义仅仅作为预先写好的代码嵌入到当前程序中,不产生函数调用,所占用的仅仅是一些空间,省去了参数压栈,生成汇编语言的 call 调用,返回参数,执行 return等过程,从而提高了程序的执行速度。虽然宏破坏了程序的可读性,使排错更加麻烦,但对于嵌入式系统,为了达到要求的性能,嵌入代码常常是必须的做法。
此外,我们还要避免不必要的函数调用,请看下面的代码:
[plain] view plain copy PRint?
1. void str_print( char *str )
2. {
3. int i;
4. for ( i = 0; i < strlen ( str ); i++ )
5. {
6. printf("%c",str[ i ] );
7. }
8. }
9. void str_print1 ( char *str )
10. {
11. int len;
12. len = strlen ( str );
13. for ( i = 0; i < len; i++ )
14. {
15. printf("%c",str[ i ] );
16. }
17. }
请注意,这两个函数的功能相似。然而,第一个函数调用strlen函数多次,而第二个函数只调用函数strlen一次。因此第二个函数性能明显比第一个好。
在 C 语言中循环语句使用频繁,提高循环体效率的基本办法就是降低循环体的复杂性:
(1) 在多重循环中,应将最长的循环放在最内层,最短的循环放在最外层。这样可以减少 CPU跨切循环的次数。如例 1-1 的效率比 1-2 的效率要低:
[plain] view plain copy print?
1. for (j = 0; j < 30; j++)
2. {
3. for (i = 0; i < 10; i++)
4. {
5. ……
6. }
7.
8. } // 例子 1-1
9. for (i = 0; i < 10; i++)
10. {
11. for (j = 0; j < 30; j++)
12. {
13. ……
14. }
15.
16. } // 例子 1-2
例 1-1长循环在外层,效率低;例 1-2长循环在内层,效率高。
(2) 如果循环体内有逻辑判断,并且循环次数大,应把循环判断移到循环体外。如例 2-1比例 2-2 多执行了 K-1 次判断,而且由于前者频繁进行判断,打断了循环"流水线"作业,使得编译器不能对循环进行优化处理,降低了效率。
[plain] view plain copy print?
1. for (i = 0; i < 10000; i++)
2. {
3. if (条件)
4. 语句;
5. else
6. 语句;
7. } // 例子 2-1 程序简洁但效率低
8. if (条件)
9. {
10. for (i = 0; i < 10000; i++)
11. 语句;
12. }
13. else
14. {
15. for (i = 0; i < 10000; i++)
16. 语句;
17. } // 例子 2-2 程序部简洁但效率高
switch 语句是 C 语言中常用的选择语句, 在编译时会产生if- else- if 嵌套代码,并按照顺序进行比较,发现匹配时,就跳转到满足条件的语句执行。
当 switch 语句中的 case 标号很多时,为了减少比较的次数,可以把发生频率相对高的条件放到第一位或者把整个 switch 语句转化嵌套 switch 语句。把发生频率高的 case 标号放在最外层的 switch 语句中,发生相对频率相对低的 case 标号放在另外的 switch 语句中。如例 3 中,把发生率高的case 标号放在外层的 switch 语句中,把发生频率低的放在缺省的(default)内层 switch 语句中。
[plain] view plain copy print?
1. switch (表达式)
2. {
3. case 值1:
4. 语句1: break;
5. case 值2:
6. 语句2:break;
7. ……
8. /*把发生频率低的放在内层的switch语句中*/
9. default:
10. switch (表达式)
11. {
12. case 值n:
13. 语句n: break;
14. case 值m:
15. 语句m: break;
16. ……
17. }
18. }
例子3 使用嵌套switch语句提高程序执行效率。
考虑Fibonacci(斐波那契)问题,Fibonacci问题是可以通过简单的递归方法来解决:
[plain] view plain copy print?
1. int fib ( n )
2. {
3. if ( n == 0 || n == 1 )
4. {
5. return 1;
6. }
7. else
8. {
9. return fib( n - 2 ) + fib ( n - 1 );
10. }
11. }
注:在这里,我们考虑Fibonacci 系列从1开始,因此,该系列看起来:1,1,2,3,5,8,…
注意:从递归树,我们计算fib(3)函数2次,fib(2)函数3次。这是相同函数的重复计算。如果n非常大,fib函数的效率会比较低。Memoization是一个简单的技术,可以被用在递归,加强计算速度。fibonacci 函数Memoization的代码如下:
[plain] view plain copy print?
1. int calc_fib ( int n )
2. {
3. int val[ n ] , i;
4. for ( i = 0; i <=n; i++ )
5. {
6. val[ i ] = -1; // Value of the first n + 1 terms of the fibonacci terms set to -1
7. }
8. val[ 0 ] = 1; // Value of fib ( 0 ) is set to 1
9. val[ 1 ] = 1; // Value of fib ( 1 ) is set to 1
10. return fib( n , val );
11. }
12.
13. int fib( int n , int* value )
14. {
15. if ( value[ n ] != -1 )
16. {
17. return value[ n ]; // Using memoization
18. }
19. else
20. {
21. value[ n ] = fib( n - 2 , value ) + fib ( n - 1 , value ); // Computing the fibonacci term
22. }
23. return value[ n ]; // Returning the value
24. }
除了编程上的技巧外,为提高系统的运行效率,我们通常也需要最大可能地利用各种硬件设备自身的特点来减小其运转开销,例如减小中断次数,利用DMA传输方式等。
对于嵌入式系统,C语言在开发速度,软件可靠性以及软件质量等方面都有着明显的优势。本文就嵌入式C语言在系统开发中,如何更好的利用系统资源,对代码进行优化进行了讨论。当然代码优化的方法还有很多,这里只是写出了一部分,希望能为开发人员提供一些帮助,也欢迎大家留言交流。
新闻热点
疑难解答