为全局变量赋值_实例分析如何远离漫天飞舞的全局变量
先談談全局變量的特點
全局變量(Global Variables):在計算機編程語言中,所謂全局變量是指具有全局作用域的變量,這意味著它在整個程序中是可見的,因此是可訪問的。所謂可訪問,是指全局可讀、全局可寫。在編譯語言中,全局變量通常是靜態變量,其范圍(生命周期)是程序的整個運行時。當然解釋性語言除外,解釋性語言包括命令行解釋器(比如python, Java script,shell等)中,全局變量通常在聲明時由解釋器動態分配,這是由于解釋性語言是讀取>解釋>執行模式,不像編譯性語言,運行前可預知變量屬性,解釋性語言讀取解釋前無從獲取變量屬性。
在C/C++編程語言中,全局變量的這種全局可見性特點,濫用全局變量會讓代碼表現當相當邪惡!如果使用全局變量,就意味著下面這些場景的存在:
- 實際代碼可能有很多地方在讀、在寫全局變量
- 全局變量在多線程或多任務間共享
- 全局變量在常規代碼和中斷服務程序間共享
為啥說全局變量很邪惡?
單片機裸機編程
或許你會說,我就這樣用?咋了?軟件也跑的很好啊?來看看這個場景:
一個超字寬的變量(比如16位單片機,字寬即為16位),正被一個常規代碼在寫變量數據域時且還沒寫完,啪嘰,來了個中斷!中斷一來,CPU趕緊把手里的活兒停下來,奔過去處理中斷了,不巧在中斷函數里,該變量因業務需求有需要寫這個變量有經驗的不這么寫,僅為了方便說明:
舉個栗子,還是以之前文章的傳感器為例,實際應用中傳感器可能是下面這樣的數據結構來描述:
#ifndef?_SENSOR_H_#define?_SENSOR_H_typedef?struct?_t_sensor{/*?測量值與測量范圍及單位有關?*/float?value;/*?測量范圍,根據采樣值映射??*/float?upper_range;float?lower_range;/*?溫度單位?*/unsiged char unit;}T_SENSOR;/*假定是一個溫度測量產品*/extern?T_SENSOR?temperature;#endif?_SENSOR_H_
假定這個傳感器數據結構有這樣一些被訪問的可能:
這些需求用例,用圖描述一下:
比如用戶操作HMI界面正改寫溫度范圍,而此時遠程上位機也正改寫溫度范圍,按上面這個做法,可能出現哪些邪惡的后果呢?
0x43488000?/*?浮點數200.5的16進制*/
假定中斷進入時,HMI界面程序寫入了0x4396前兩個字節,中斷返回時,上限改寫為200.5(0x43488000),此時繼續執行后面兩個字節寫入,則上限變成為(0x43484000),來看看這個數是多大?變成了200.25,這是不是很邪惡?
或許有的朋友會說,可以在LCD寫范圍時關中斷嘛。誠然,可以這么做:
void?hmi_operate(){????/*關中斷*/
????_disable_interrupt();
????/*改寫溫度范圍*/
????....
????/*開中斷*/
????_enable_interrupt();
}
但是如果這個全局變量有很多地方在改寫,為了數據安全,勢必就到處開/關中斷,這樣做的壞處:
- 經常開關中斷,勢必影響中斷響應,會有概率丟失異步中斷處理(比如串口按字節接收中斷,可能就會漏收字節),程序不健壯,工作不穩定。
- 到處訪問改寫,不易調試,群魔亂舞,代碼也不易維護。想加點東西,改點東西可能隨處都是坑,一不小心就掉坑里去了!
- 初學者甚至不會用struct將相關的數據包在一起,其結果是代碼里到處都是基本類型的全局變量。一些簡單的業務邏輯實現變成一個復雜的代碼,數據信息流向一團亂麻。
裸機程序策略
對于上面這樣一個應用場景,怎么解決這種混亂的現象呢。這里分享一下我的思路,這里將主要的串口以及測量模塊的設計思路用UML圖描述一下大體思路:
如此一來,外部就看不到全局變量了,只需要調用對應的set/get方法即可實現讀寫訪問,由于是裸機前后臺程序,數據流向就變的非常清晰了。main函數的主循環大致就可能是這樣:
void?main(void){???/*模塊初始化*/
???init_uart();
???init_temperature();
???....
???
???while(1)
???{
???????interprete_uart();
???????/*可能是周期性調用*/
???????if(timer_100ms)
???????{
??????????timer_100ms?=?0;
??????????update_temperature();
???????}????????
?????????
???????....
???}???
}
那么uart協議解析要怎么做呢?
void?interprete_uart(void){????if(rx_msg.flag)
????{
????????rx_msg.flag?=?false;
????????/*報文完整性檢查*/
????????...
????????????
????????/*設置溫度配置*/
????????set_upper_range(xxx);
????????set_lower_range(xxx);
????????set_unit(xxx);
????}
????
????if(tx_msg.flag)
????{
????????tx_msg.flag?=?false;
????????start_send();
????}
}
static?start_send(T_UART_MSG?*pMsg){
????/*負責底層操作,啟動中斷傳輸*/
}
/*提供應答數據接口*/
void?reply_temperature_setting(T_SENSOR?sensor){
????/*解析傳入參數并封裝應答報文*/
}
如此一來,數據流向將變得很清晰,串口接收到數據更新范圍配置時,也無需開關中斷了,從應用角度幾乎見不到全局變量。當然這樣做的代價就是會增加一些棧開銷。但是這種代價還是值得的。
對于測量模塊的set函數思路稍做說明:
int?set_upper_range(float?range){????T_SENSOR?temp?=?temperature;
????temp.upper_range?=?range;
????/*實現范圍合理性檢查*/
????if(check_range(temp))
????{
????????/*兩個結構體變量可以直接賦值*/
????????temperature?=?temp;
????????return?0;
????}
????else
????{
???????return?-1;
????}
}
int?set_unit(E_UNIT?unit){
????if(unit>E_UNIT_F)
????????return?-1;
????adjust_range(&temperature,unit);
????temperature.unit?=?unit;????
}
上述代碼旨在分享個人的一些思路,其中或有不夠嚴謹的地方,但通過這樣的設計思路,應能大幅度遠離滿天飛的全局變量。
多任務/多線程環境
上面描述其實本質上描述了裸機程序里,普通模式運行程序與中斷服務程序對于臨界資源的競爭。事實上現在不管是單片機,還是處理器,大多都是基于一個操作系統進行應用開發。甚至還可能是多核芯片,這里就存在并發競爭訪問資源的問題。
臨界資源:各任務/線程采取互斥的方式,實現共享的資源稱作臨界資源。屬于臨界資源的硬件串口打印、顯示等,軟件有消息緩沖隊列、變量、數組、緩沖區等。多任務/線程間應采取互斥方式,從而實現對這種資源的共享。
多任務/多線程情況下在寫模塊時,只需要封裝進保護機制即可。常見的保護機制有關中斷、信號量、互斥鎖等。在Linux內核中為應對多核并發訪問還有自旋鎖機制。由于篇幅所限,本文就不做展開了,先挖個坑,以后有機會再分享吧。
總結一下
在前文介紹static文章的基礎上,相對更深入的介紹了為何需要隱藏屬性以及開放接口的做法。以及如何遠離邪惡的全局變量漫天飛舞的不良設計風格。
辛苦原創總結,如果覺得有價值也請幫忙點贊/在看/轉發支持,不勝感激!
—END—
往期精彩推薦,點擊即可閱讀猜你喜歡
干貨 | 嵌入式必備技能之Git的使用
CPU中的程序是怎么運行起來的
嵌入式系統軟件架構設計
Linux下應用開發基礎
【Linux筆記】Pinctrl子系統與GPIO子系統
總結
以上是生活随笔為你收集整理的为全局变量赋值_实例分析如何远离漫天飞舞的全局变量的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: iOS 16 如何为第三方应用开启“实时
- 下一篇: iis web.config 配置 经典