為了做一個(gè)好的設(shè)計(jì),本身在軟硬件的配合上就需要克服無(wú)數(shù)的困難和障礙,任何一名 MCU 愛(ài)好者都不希望遇到一些因?yàn)檎Z(yǔ)言和工具而產(chǎn)生的困擾,我們?cè)?MCU 這種資源受限的平臺(tái)上進(jìn)行 C語(yǔ)言的開發(fā)雖然被軟件界看起來(lái)不怎么高大上,但是 MCU 的開發(fā)目前 C 語(yǔ)言還是主流,為了更好的操控和調(diào)試我們的硬件,我們還是需要竭力的避免一些 C語(yǔ)言編程的陷阱,避免被一些高大上的變成語(yǔ)言或者架構(gòu)干擾產(chǎn)品整體的進(jìn)度和可靠性。
第1坑:不要使用“GOTO”語(yǔ)句
GOTO 語(yǔ)句最早源于匯編語(yǔ)言的跳轉(zhuǎn),在很多年前,計(jì)算機(jī)的變成還處于起步階段,C語(yǔ)言開始也是尋著匯編的思路來(lái)設(shè)計(jì)的,因此就遺留下了這么一個(gè) GOTO 語(yǔ)句,允許程序員自由的在代碼間翱翔。使用GOTO語(yǔ)句的例子
int main() {
int i = 0;
// 使用goto語(yǔ)句的簡(jiǎn)單示例
goto start;
loop:
printf("Inside loop: %dn", i);
i++;
start:
if (i < 5)
goto loop;
printf("Loop finished.n");
return 0;
}
這種 goto 語(yǔ)句用起來(lái)簡(jiǎn)單,但是整體程序如果來(lái)回跳轉(zhuǎn),讀起來(lái)會(huì)非常的困難,非常繞,并且 GOTO 語(yǔ)句還存在以下問(wèn)題:
- 可讀性差: 使用
goto
語(yǔ)句的代碼通常會(huì)變得難以理解,因?yàn)樗试S在程序中跳轉(zhuǎn)到不同的標(biāo)簽位置。這使得代碼流程變得不清晰,增加了理解代碼的難度。 - 難以維護(hù): 當(dāng)代碼包含大量
goto
語(yǔ)句時(shí),很容易導(dǎo)致代碼的維護(hù)困難。修改代碼或添加新功能時(shí),必須仔細(xì)考慮goto
語(yǔ)句的影響,以防止引入錯(cuò)誤。 - 錯(cuò)誤的使用可能導(dǎo)致問(wèn)題: 如果不小心使用了錯(cuò)誤的標(biāo)簽,或者在不當(dāng)?shù)奈恢檬褂?code>goto,可能導(dǎo)致程序的不正確行為。這種錯(cuò)誤可能難以追蹤和修復(fù)。
- 不利于結(jié)構(gòu)化編程: 使用
goto
語(yǔ)句可能違背結(jié)構(gòu)化編程的原則,使得代碼難以按照清晰的結(jié)構(gòu)組織。結(jié)構(gòu)化編程強(qiáng)調(diào)使用順序結(jié)構(gòu)、選擇結(jié)構(gòu)和循環(huán)結(jié)構(gòu)來(lái)構(gòu)建清晰、可讀、可維護(hù)的代碼。 - 不利于調(diào)試: 調(diào)試時(shí),跳轉(zhuǎn)語(yǔ)句會(huì)使程序的執(zhí)行路徑變得復(fù)雜,增加了調(diào)試的難度。代碼中的跳轉(zhuǎn)可能使得代碼不易于單步調(diào)試,阻礙了查找和修復(fù)錯(cuò)誤的過(guò)程。
第2坑:使用完整的條件語(yǔ)句
在使用判斷語(yǔ)句的時(shí)候,我們尤其要注意判斷條件的完整性,我們?cè)S多工程師都熟悉簡(jiǎn)單的if else 語(yǔ)句,然而有一些工程師卻沒(méi)有注意到,不同的寫法可能會(huì)浪費(fèi)一些處理器的時(shí)間。比如:
if(value == 1U)
{
}
if(value == 0U)
{
}
if(value == 1U)
{
}
else
{
}
在第一種寫法中,處理器會(huì)去判斷兩次,然后根據(jù)判斷結(jié)果進(jìn)行分支運(yùn)行,但是如果我們寫成第二種寫法,處理器只需要判斷一次就可以了。尤其是這種判斷在一個(gè)大循環(huán)內(nèi)部,這將浪費(fèi)我們很多處理器時(shí)間。
另外為了代碼具備更清晰的可讀性,我們應(yīng)該讓 if else 成對(duì)出現(xiàn),并且都是用{}把程序分割開來(lái),這樣也避免我們?cè)谡{(diào)試的時(shí)候復(fù)制粘貼出現(xiàn)一些錯(cuò)誤,從而影響我們調(diào)試和解決問(wèn)題的進(jìn)度。
int main() {
int choice;
// 提示用戶輸入數(shù)字
printf("Enter a number (1-3): ");
scanf("%d", &choice);
// 使用 switch 語(yǔ)句根據(jù)用戶輸入執(zhí)行不同的操作
switch (choice) {
case 1:
printf("You chose option 1.n");
// 執(zhí)行操作1的代碼
break;
case 2:
printf("You chose option 2.n");
// 執(zhí)行操作2的代碼
break;
case 3:
printf("You chose option 3.n");
// 執(zhí)行操作3的代碼
break;
default:
printf("Invalid choice. Please enter a number between 1 and 3.n");
// 處理無(wú)效選擇的代碼
break;
}
return 0;
}
如果判斷分支比較多,一定是用 swich case 語(yǔ)句來(lái)代替 if else。道理是相同的,一定要完整且用{}將程序段分隔好。同時(shí)要注意,如果我們對(duì)分支的命中率有一定的前瞻性,那么我們最好把命中率比較高的分支放在前面。
對(duì)于 case 比較多的情況,有些編譯器會(huì)主動(dòng)優(yōu)化,這時(shí)候就不必考慮命中率的問(wèn)題了。
第3坑:使用FOR(;;)還是 While(1)?
MCU 的開發(fā)過(guò)程中,我們絕大部分情況下還是在使用前后臺(tái)系統(tǒng),當(dāng)然即便我們跑了一些實(shí)時(shí)性的操作系統(tǒng),也避免不了使用一些無(wú)限循環(huán)的處理。
那么處理無(wú)限循環(huán)的語(yǔ)句目前有兩種寫法,我??吹揭恍┏跫?jí)工程師會(huì)使用 while(1),而在一些操作系統(tǒng)源碼中看到的更多的是 for(;;)。
如果在 C99 的版本下,我們使用 for 來(lái)寫循環(huán)看起來(lái)更緊湊。
// while 循環(huán)的初始化
int i = 0;
while (i < 5) {
// ...
i++;
}
// for 循環(huán)的初始化
for (int i = 0; i < 5; i++) {
// ...
}
另外,我十幾年前在賽普拉斯的單片機(jī)上開發(fā),因?yàn)?flash 空間很小,需要極致優(yōu)化代碼來(lái)進(jìn)行空間壓縮,這里我選擇了 for 循環(huán)的寫法可以讓空間多出一個(gè)字節(jié)來(lái),不過(guò)現(xiàn)在的很多編譯器都已經(jīng)更新了很多年了,至少在主流的 arm 平臺(tái)上他們的匯編代碼都是一樣的了。
-
mcu
+關(guān)注
關(guān)注
146文章
17984瀏覽量
367007 -
嵌入式
+關(guān)注
關(guān)注
5152文章
19675瀏覽量
317620 -
C語(yǔ)言
+關(guān)注
關(guān)注
180文章
7632瀏覽量
141777 -
編程
+關(guān)注
關(guān)注
88文章
3689瀏覽量
95260
發(fā)布評(píng)論請(qǐng)先 登錄
評(píng)論