- 相關推薦
嵌入式C語言優(yōu)化小技巧
C語言是一門通用計算機編程語言,應用廣泛。下面小編整理了嵌入式C語言優(yōu)化技巧,希望對大家有幫助!
1、嵌入式C語言的特點
作為一種結(jié)構(gòu)化程序設計語言,C 語言兼顧多種高級語言的特點,具有很強的功能性和可移植性。但在嵌入式系統(tǒng)開發(fā)中,出于對低價產(chǎn)品的需求,系統(tǒng)的計算能力和存儲容量都非常有限,因此如何利用好這些資源就顯得十分重要。開發(fā)人員應注意嵌入式 C語言和標準 C 語言的區(qū)別,減少生成代碼長度,提高程序執(zhí)行效率,在程序設計中對代碼進行優(yōu)化。
2、C代碼在程序中的優(yōu)化
現(xiàn)在的 C 編譯器會自動對代碼進行優(yōu)化,但這些優(yōu)化是對執(zhí)行速度和代碼長度的平衡。如果要獲得更小且執(zhí)行效率更高的代碼,需要程序員手工對代碼進行優(yōu)化。
3、變量類型的定義
不同的數(shù)據(jù)類型所生成的機器代碼長度相差很多,變量類型選取的范圍越小運行速度越快,占用的內(nèi)存越少。能夠使用字符型(char)定義的變量,就不要使用整型(int)變量來定義;能夠使用整型變量定義的變量就不要用長整型(long int),能不使用浮點型(float)變量就不要使用浮點型變量。相同類型的數(shù)據(jù)類型,有無符號對機器代碼長度也有影響。因此我們應按照實際需要合理的選用數(shù)據(jù)類型。當然,在定義變量后不要超過變量的作用范圍,如果超過變量的范圍賦值,C編譯器并不報錯,但程序運行結(jié)果卻錯了,而且這樣的錯誤很難發(fā)現(xiàn)。
4、算法優(yōu)化
算法優(yōu)化指對程序時空復雜度的優(yōu)化:在 PC 機上進行程序設計時一般不必過多關注程序代碼的長短,只需考慮功能的實現(xiàn),但嵌入式系統(tǒng)就必須考慮系統(tǒng)的硬件資源,在程序設計時,應盡量采用生成代碼短的算法,在不影響程序功能實現(xiàn)的情況下優(yōu)化算法。
5、適當?shù)氖褂煤?/strong>
在C程序中使用宏代碼可以提高程序的執(zhí)行效率。宏代碼本身不是函數(shù),但使用起來像函數(shù)。函數(shù)調(diào)用要使用系統(tǒng)的棧來保存數(shù)據(jù),同時 CPU 在函數(shù)調(diào)用時需要保存和恢復當前的現(xiàn)場,進行進棧和出棧操作,所以函數(shù)調(diào)用也需要 CPU時間。而宏定義就沒有這個問題:宏定義僅僅作為預先寫好的代碼嵌入到當前程序中,不產(chǎn)生函數(shù)調(diào)用,所占用的僅僅是一些空間,省去了參數(shù)壓棧,生成匯編語言的 call 調(diào)用,返回參數(shù),執(zhí)行 return等過程,從而提高了程序的執(zhí)行速度。雖然宏破壞了程序的可讀性,使排錯更加麻煩,但對于嵌入式系統(tǒng),為了達到要求的性能,嵌入代碼常常是必須的做法。
此外,我們還要避免不必要的函數(shù)調(diào)用,請看下面的代碼:
[plain] view plain copy print?
void str_print( char *str )
{
int i;
for ( i = 0; i < strlen ( str ); i++ )
{
printf("%c",str[ i ] );
}
}
void str_print1 ( char *str )
{
int len;
len = strlen ( str );
for ( i = 0; i < len; i++ )
{
printf("%c",str[ i ] );
}
}
請注意,這兩個函數(shù)的功能相似。然而,第一個函數(shù)調(diào)用strlen函數(shù)多次,而第二個函數(shù)只調(diào)用函數(shù)strlen一次。因此第二個函數(shù)性能明顯比第一個好。
6、內(nèi)嵌匯編
程序中對時間要求苛刻的部分可以用內(nèi)嵌匯編來重寫,以帶來速度上的顯著提高。但是,開發(fā)和測試匯編代碼是一件辛苦的工作,它將花費更長的時間,因而要慎重選擇要用匯編的部分。在程序中,存在一個80-20原則,即20%的程序消耗了80%的運行時間,因而我們要改進效率,最主要是考慮改進那20%的代碼。
7、提高循環(huán)語言的效率
在 C 語言中循環(huán)語句使用頻繁,提高循環(huán)體效率的基本辦法就是降低循環(huán)體的復雜性:
(1) 在多重循環(huán)中,應將最長的循環(huán)放在最內(nèi)層,最短的循環(huán)放在最外層。這樣可以減少 CPU跨切循環(huán)的次數(shù)。如例 1-1 的效率比 1-2 的效率要低:
[plain] view plain copy print?
for (j = 0; j < 30; j++)
{
for (i = 0; i < 10; i++)
{……}
} // 例子 1-1
for (i = 0; i < 10; i++)
{
for (j = 0; j < 30; j++)
{……}
} // 例子 2-2 程序部簡潔但效率高
8、提高 switch 語句的效率
switch 語句是 C 語言中常用的選擇語句, 在編譯時會產(chǎn)生if- else- if 嵌套代碼,并按照順序進行比較,發(fā)現(xiàn)匹配時,就跳轉(zhuǎn)到滿足條件的語句執(zhí)行。
當 switch 語句中的 case 標號很多時,為了減少比較的次數(shù),可以把發(fā)生頻率相對高的條件放到第一位或者把整個 switch 語句轉(zhuǎn)化嵌套 switch 語句。把發(fā)生頻率高的 case 標號放在最外層的 switch 語句中,發(fā)生相對頻率相對低的 case 標號放在另外的 switch 語句中。如例 3 中,把發(fā)生率高的case 標號放在外層的 switch 語句中,把發(fā)生頻率低的放在缺省的(default)內(nèi)層 switch 語句中。
[plain] view plain copy print?
switch (表達式)
{
case 值1:
語句1: break;
case 值2:
語句2:break;
……
/*把發(fā)生頻率低的放在內(nèi)層的switch語句中*/
default:
switch (表達式)
{
case 值n:
語句n: break;
case 值m:
語句m: break;
……
}
}
例子3 使用嵌套switch語句提高程序執(zhí)行效率。
9、避免使用標準庫
使用 C語言標準庫可以加快開發(fā)進度,但由于標準庫需要設法處理用戶所有可能遇到的情況,所以很多標準庫代碼很大。比如標準庫中的 sprintf函數(shù)非常大。這個龐大的代碼中有很大一部分用于處理浮點數(shù),如果程序中不需要格式化浮點數(shù)值( 如%f),程序設計人員就可以根據(jù)實際情況用少量的代碼實現(xiàn)這個功能。
10、采用數(shù)學方法優(yōu)化程序
數(shù)學是計算機之母,沒有數(shù)學的依據(jù)和基礎,就沒有計算機的發(fā)展,所以在編寫程序的時候,采用一些數(shù)學方法會對程序的執(zhí)行效率有數(shù)量級的提高。有時候這個問題常常被大家忽略, 對于沒有經(jīng)驗的程序員來說更是如此。例如:求 1~100 的和:
sum = 100*(100+1)/2;
數(shù)學公式: (a1 + an)*n/2
使用C語言的位操作可以減少除法和取模的運算。在計算機程序中數(shù)據(jù)的位是可以操作的最小數(shù)據(jù)單位,理論上可以用“位運算”來完成所有的運算和操作。因而,靈活的位操作可以有效地提高程序運行的效率。比如用用位操作區(qū)代替除法:比如:128 / 8 ->> 128 >> 3;
優(yōu)化算法和數(shù)據(jù)結(jié)構(gòu)對提高代碼的效率有很大的幫助。當然有時候時間效率和空間效率是對立的,此時應分析哪個更重要, 做出適當?shù)恼壑。另外,在進行優(yōu)化的時候不要片面的追求緊湊的代碼,因為緊湊的代碼并不能產(chǎn)生高效率的機器碼。
11、存儲器分配
由于成本限制,嵌入式系統(tǒng)存儲器容量有限。程序中所有的變量,包含的庫函數(shù)以及堆棧等都使用有限的內(nèi)存:全局變量在整個程序范圍內(nèi)都有效。程序執(zhí)行完后才會釋放;靜態(tài)變量的作用范圍也是整個程序,只有局部變量中的動態(tài)變量在函數(shù)執(zhí)行完后會釋放。因此, 在程序中應盡量使用局部變量,提高內(nèi)存使用效率。程序中堆的大小受限于所有全局數(shù)據(jù)和?臻g都分配后的剩余量,如果堆太小,程序不能夠在需要的時候分配內(nèi)存。因此在使用 malloc 函數(shù)申請內(nèi)存之后一定要用 free 函數(shù)進行釋放, 防止內(nèi)存泄露。
12、選擇好的無限循環(huán)
在編程中,我們常常需要用到無限循環(huán),常用的兩種方法是while (1) 和 for (;;)。這兩種方法效果完全一樣,但那一種更好呢?然我們看看它們編譯后的代碼:
編譯前:
while (1);
編譯后:
mov eax,1
test eax,eax
je foo+23h
jmp foo+18h
編譯前:
for (;;);
編譯后:
jmp foo+23h
顯然,for (;;)指令少,不占用寄存器,而且沒有判斷,跳轉(zhuǎn),比while (1)好。
13、使用Memoization,以避免遞歸重復計算
考慮Fibonacci(斐波那契)問題,F(xiàn)ibonacci問題是可以通過簡單的遞歸方法來解決:
[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)函數(shù)2次,fib(2)函數(shù)3次。這是相同函數(shù)的重復計算。如果n非常大,fib函數(shù)的效率會比較低。Memoization是一個簡單的技術(shù),可以被用在遞歸,加強計算速度。fibonacci 函數(shù)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. }
除了編程上的技巧外,為提高系統(tǒng)的運行效率,我們通常也需要最大可能地利用各種硬件設備自身的特點來減小其運轉(zhuǎn)開銷,例如減小中斷次數(shù),利用DMA傳輸方式等。
【嵌入式C語言優(yōu)化小技巧】相關文章:
嵌入式C語言優(yōu)化技巧10-27
嵌入式C語言性能優(yōu)化方法10-22
嵌入式C語言編程小知識12-20
C語言高效編程的小技巧09-13
C語言高效編程的4個小技巧10-27
嵌入式C語言學習秘訣08-25
C語言自學入門技巧09-17
C語言左右法則的技巧10-03
C語言宏定義技巧09-03