主頁 > 軟體設計 > 嵌入式開發中的防御性C語言編程

嵌入式開發中的防御性C語言編程

2021-08-15 08:06:20 軟體設計

嵌入式產品的可靠性自然與硬體密不可分,但在硬體確定、并且沒有第三方測驗的前提下,使用防御性編程思想寫出的代碼,往往具有更高的穩定性,

防御性編程首先需要認清C語言的種種缺陷和陷阱,C語言對于運行時的檢查十分弱小,需要程式員謹慎的考慮代碼,在必要的時候增加判斷;防御性編程的另一個核心思想是假設代碼運行在并不可靠的硬體上,外接干擾有可能會打亂程式執行順序、更改RAM存盤資料等等,

1.具有形參的函式,需判斷傳遞來的實參是否合法

程式員可能無意識的傳遞了錯誤引數;外界的強干擾可能將傳遞的引數修改掉,或者使用隨機引數意外的呼叫函式,因此在執行函式主體前,需要先確定實參是否合法,

2. 仔細檢查函式的回傳值

對函式回傳的錯誤碼,要進行全面仔細處理,必要時做錯誤記錄,

3. 防止指標越界

如果動態計算一個地址時,要保證被計算的地址是合理的并指向某個有意義的地方,特別對于指向一個結構或陣列的內部的指標,當指標增加或者改變后仍然指向同一個結構或陣列,

4. 防止陣列越界

陣列越界的問題前文已經講述的很多了,由于C不會對陣列進行有效的檢測,因此必須在應用中顯式的檢測陣列越界問題,下面的例子可用于中斷接收通訊資料,

在使用一些庫函式時,同樣需要對邊界進行檢查,比如下面的memset(RecBuf,0,len)函式把RecBuf指指向的記憶體區的前len個位元組用0填充,如果不注意len的長度,就會將陣列RecBuf之外的記憶體區清零:

5. 數學算數運算

5.1除法運算,只檢測除數為零就可靠嗎?

除法運算前,檢查除數是否為零幾乎已經成為共識,但是僅檢查除數是否為零就夠了嗎?

考慮兩個整數相除,對于一個signed long型別變數,它能表示的數值范圍為:-2147483648 ~+2147483647,如果讓-2147483648/ -1,那么結果應該是+2147483648,但是這個結果已經超出了signedlong所能表示的范圍了,所以,在這種情況下,除了要檢測除數是否為零外,還要檢測除法是否溢位,

#include <limits.h>

signed long sl1,sl2,result;

/*初始化sl1和sl2*/

if((sl2==0)||(sl1==LONG_MIN && sl2==-1))

{

//處理錯誤

}

else

{

result = sl1 / sl2;

}

5.2檢測運算溢位

整數的加減乘運算都有可能發生溢位,在討論未定義行為時,給出過一個有符號整形加法溢位判斷代碼,這里再給出一個無符號整形加法溢位判斷代碼段:

#include <limits.h>

unsigned int a,b,result;

/*初始化a,b*/

if(UINT_MAX-a<b)

{

//處理溢位

}

else

{

result=a+b;

}

嵌入式硬體一般沒有浮點處理器,浮點數運算在嵌入式也比較少見并且溢位判斷嚴重依賴C庫支持,這里不討論,

5.3檢測移位

在討論未定義行為時,提到有符號數右移、移位的數量是負值或者大于運算元的位數都是未定義行為,也提到不對有符號數進行位操作,但要檢測移位的數量是否大于運算元的位數,下面給出一個無符號整數左移檢測代碼段:

unsigned int ui1;

unsigned int ui2;

unsigned int uresult;

/*初始化ui1,ui2*/

if(ui2>=sizeof(unsigned int)*CHAR_BIT)

{

//處理錯誤

}

else

{

uresult=ui1<<ui2;

}

6. 如果有硬體看門狗,則使用它

在其它一切措施都失效的情況下,看門狗可能是最后的防線,它的原理特別簡單,但卻能大大提高設備的可靠性,如果設備有硬體看門狗,一定要為它撰寫驅動程式,

·要盡可能早的開啟看門狗

這是因為從上電復位結束到開啟看門狗的這段時間內,設備有可能被干擾而跳過看門狗初始化程式,導致看門狗失效,盡可能早的開啟看門狗,可以降低這種概率;

·不要在中斷中喂狗,除非有其他聯動措施

在中斷程式喂狗,由于干擾的存在,程式可能一直處于中斷之中,這樣會導致看門狗失效,如果在主程式中設定標志位,中斷程式喂狗時與這個標志位聯合判斷,也是允許的;

·喂狗間隔跟產品需求有關,并非特定的時間

產品的特性決定了喂狗間隔,對于不涉及安全性、實時性的設備,喂狗間隔比較寬松,但間隔時間不宜過長,否則被用戶感知到,是影響用戶體驗的,對于設計安全性、有實時控制類的設備,原則是盡可能快的復位,否則會造成事故,

克萊門汀號在進行第二階段的任務時,原本預訂要從月球飛行到太空深處的Geographos小行星進行探勘,然而這艘太空探測器在飛向小行星時卻由于一個軟體缺陷而使其中斷運作20分鐘,不但未能到達小行星,也因為控制噴嘴燃燒了11分鐘使電力供應降低,無法再透過遠端控制探測器,最終結束這項任務,但也導致了資源與資金的浪費,

“克萊門汀太空任務失敗這件事讓我感到十分震驚,它其實可以透過硬體中一款簡單的看門狗計時器避免掉這項意外,但由于當時的開發時間相當緊縮,程式設計人員沒時間撰寫程式來啟動它,”Ganssle說,

遺憾的是,1998年發射的近地號太空船(NEAR)也遇到了相同的問題,由于編程人員并未采納建議,因此,當推進器減速器系統故障時,29公斤的儲備燃料也隨之報銷──這同樣是一個本來可經由看門狗定時器編程而避免的問題,同時也證明要從其他程式設計人員的錯誤中學習并不容易,

7. 關鍵資料儲存多個備份,取資料采用“表決法”

RAM中的資料在受到干擾情況下有可能被改變,對于系統關鍵資料應該進行保護,關鍵資料包括全域變數、靜態變數以及需要保護的資料區域,備份資料與原資料不應該處于相鄰位置,因此不應由編譯器默認分配備份資料位置,而應該由程式員指定區域存盤,

可以將RAM分為3個區域,第一個區域保存原碼,第二個區域保存反碼,第三個區域保存異或碼,區域之間預留一定量的“空白”RAM作為隔離,可以使用編譯器的“分散加載”機制將變數分別存盤在這些區域,需要進行讀取時,同時讀出3份資料并進行表決,取至少有兩個相同的那個值,

假如設備的RAM從0x1000_0000開始,我需要在RAM的0x1000_0000~0x10007FFF記憶體儲原碼,在0x1000_9000~0x10009FFF記憶體儲反碼,在0x1000_B000~0x1000BFFF記憶體儲0xAA的異或碼,編譯器的分散加載可以設定為:

LR_IROM1 0x00000000 0x00080000 { ; load region size_region

ER_IROM1 0x00000000 0x00080000 { ; load address = execution address

*.o (RESET, +First)

*(InRoot$$Sections)

.ANY (+RO)

}

RW_IRAM1 0x10000000 0x00008000 { ;保存原碼

.ANY (+RW +ZI )

}

RW_IRAM3 0x10009000 0x00001000{ ;保存反碼

.ANY (MY_BK1)

}

RW_IRAM2 0x1000B000 0x00001000 { ;保存異或碼

.ANY (MY_BK2)

}

}

如果一個關鍵變數需要多處備份,可以按照下面方式定義變數,將三個變數分別指定到三個不連續的RAM區中,并在定義時按照原碼、反碼、0xAA的異或碼進行初始化,

uint32 plc_pc=0; //原碼

__attribute__((section("MY_BK1"))) uint32 plc_pc_not=~0x0; //反碼

__attribute__((section("MY_BK2"))) uint32 plc_pc_xor=0x0^0xAAAAAAAA; //異或碼

當需要寫這個變數時,這三個位置都要更新;讀取變數時,讀取三個值做判斷,取至少有兩個相同的那個值,

為什么選取異或碼而不是補碼?這是因為MDK的整數是按照補碼存盤的,正數的補碼與原碼相同,在這種情況下,原碼和補碼是一致的,不但起不到冗余作用,反而對可靠性有害,比如存盤的一個非零整數區因為干擾,RAM都被清零,由于原碼和補碼一致,按照3取2的“表決法”,會將干擾值0當做正確的資料,

8. 對非易失性存盤器進行備份存盤

非易失性存盤器包括但不限于Flash、EEPROM、鐵電,僅僅將寫入非易失性存盤器中的資料再讀出校驗是不夠的,強干擾情況下可能導致非易失性存盤器內的資料錯誤,在寫非易失性存盤器的期間系統掉電將導致資料丟失,因干擾導致程式跑飛到寫非易失性存盤器函式中,將導致資料存盤紊亂,

一種可靠的辦法是將非易失性存盤器分成多個區,每個資料都將按照不同的形式寫入到這些磁區中,需要進行讀取時,同時讀出多份資料并進行表決,取相同數目較多的那個值,

9. 軟體鎖

對于初始化序列或者有一定先后順序的函式呼叫,為了保證呼叫順序或者確保每個函式都被呼叫,我們可以使用環環相扣,實質上這也是一種軟體鎖,此外對于一些安全關鍵代碼陳述句(是陳述句,而不是函式),可以給它們設定軟體鎖,只有持有特定鑰匙的,才可以訪問這些關鍵代碼,也可以通俗的理解為,關鍵安全代碼不能按照單一條件執行,要額外的多設定一個標志,

比如,向Flash寫一個資料,我們會判斷資料是否合法、寫入的地址是否合法,計算要寫入的扇區,之后呼叫寫Flash子程式,在這個子程式中,判斷扇區地址是否合法、資料長度是否合法,之后就要將資料寫入Flash,

由于寫Flash陳述句是安全關鍵代碼,所以程式給這些陳述句上鎖:必須具有正確的鑰匙才可以寫Flash,這樣即使是程式跑飛到寫Flash子程式,也能大大降低誤寫的風險,

/***************************************************************

* 名稱:RamToFlash()

* 功能:復制RAM的資料到FLASH,命令代碼51,

* 入口引數:dst 目標地址,即FLASH起始地址,以512位元組為分界

* src 源地址,即RAM地址,地址必須字對齊

* no 復制位元組個數,為512/1024/4096/8192

* ProgStart 軟體鎖標志

* 出口引數:IAP回傳值(paramout緩沖區) CMD_SUCCESS,SRC_ADDR_ERROR,DST_ADDR_ERROR,

SRC_ADDR_NOT_MAPPED,DST_ADDR_NOT_MAPPED,COUNT_ERROR,BUSY,未選擇扇區

****************************************************************/

void RamToFlash(uint32 dst, uint32 src, uint32 no,uint8 ProgStart)

{

PLC_ASSERT("Sector number",(dst>=0x00040000)&&(dst<=0x0007FFFF));

PLC_ASSERT("Copy bytes number is 512",(no==512));

PLC_ASSERT("ProgStart==0xA5",(ProgStart==0xA5));

paramin[0] = IAP_RAMTOFLASH; // 設定命令字

paramin[1] = dst; // 設定引數

paramin[2] = src;

paramin[3] = no;

paramin[4] = Fcclk/1000;

if(ProgStart==0xA5) //只有軟體鎖標志正確時,才執行關鍵代碼

{

iap_entry(paramin, paramout); // 呼叫IAP服務程式

ProgStart=0;

}

else

{

paramout[0]=PROG_UNSTART;

}

}

該程式段是編程lpc1778內部Flash,其中呼叫IAP程式的函式iap_entry(paramin, paramout)是關鍵安全代碼,所以在執行該代碼前,先判斷一個特定設定的安全鎖標志ProgStart,只有這個標志符合設定值,才會執行編程Flash操作,如果因為意外程式跑飛到該函式,由于ProgStart標志不正確,是不會對Flash進行編程的,

10. 通信

通訊線上的資料誤碼相對嚴重,通訊線越長,所處的環境越惡劣,誤碼會越嚴重,拋開硬體和環境的作用,我們的軟體應能識別錯誤的通訊資料,對此有一些應用措施:

·制定協議時,限制每幀的位元組數;

每幀位元組數越多,發生誤碼的可能性就越大,無效的資料也會越多,對此以太網規定每幀資料不大于1500位元組,高可靠性的CAN收發器規定每幀資料不得多于8位元組,對于RS485,基于RS485鏈路應用最廣泛的Modbus協議一幀資料規定不超過256位元組,因此,建議制定內部通訊協議時,使用RS485時規定每幀資料不超過256位元組;

·使用多種校驗

撰寫程式時應使能奇偶校驗,每幀超過16位元組的應用,建議至少撰寫CRC16校驗程式,

·增加額外判斷

1)增加緩沖區溢位判斷,這是因為資料接收多是在中斷中完成,編譯器檢測不出緩沖區是否溢位,需要手動檢查,在上文介紹資料溢位一節中已經詳細說明,

2)增加超時判斷,當一幀資料接收到一半,長時間接收不到剩余資料,則認為這幀資料無效,重新開始接收,可選,跟不同的協議有關,但緩沖區溢位判斷必須實作,這是因為對于需要幀頭判斷的協議,上位機可能發送完幀頭后突然斷電,重啟后上位機是從新的幀開始發送的,但是下位機已經接收到了上次未發送完的幀頭,所以上位機的這次幀頭會被下位機當成正常資料接收,這有可能造成資料長度欄位為一個很大的值,填滿該長度的緩沖區需要相當多的資料(比如一幀可能1000位元組),影響回應時間;另一方面,如果程式沒有緩沖區溢位判斷,那么緩沖區很可能溢位,后果是災難性的,

·重傳機制

如果檢測到通訊資料發生了錯誤,則要有重傳機制重新發送出錯的幀,

11. 開關量輸入的檢測、確認

開關量容易受到尖脈沖干擾,如果不進行濾除,可能會造成誤動作,一般情況下,需要對開關量輸入信號進行多次采樣,并進行邏輯判斷直到確認信號無誤為止,

12. 開關量輸出

開關信號簡單的一次輸出是不安全的,干擾信號可能會翻轉開關量輸出的狀態,采取重復重繪輸出可以有效防止電平的翻轉,

13. 初始化資訊的保存和恢復

微處理器的暫存器值也可能會因外界干擾而改變,外設初始化值需要在暫存器中長期保存,最容易被破壞,由于Flash中的資料相對不易被破壞,可以將初始化資訊預先寫入Flash,待程式空閑時比較與初始化相關的暫存器值是否被更改,如果發現非法更改則使用Flash中的值進行恢復,

公司目前使用的4.3寸LCD顯示屏抗干擾能力一般,如果顯示屏與控制器之間的排線距離過長或者對使用該顯示屏的設備打靜電或者脈沖群,顯示屏有可能會花屏或者白屏,

對此,我們可以將初始化顯示屏的資料保存在Flash中,程式運行后,每隔一段時間從顯示屏的暫存器讀出當前值和Flash存盤的值相比較,如果發現兩者不同,則重新初始化顯示屏,下面給出校驗原始碼,僅供參考,

定義資料結構:

定義const修飾的結構體變數,存盤LCD部分暫存器的初始值,這個初始值跟具體的應用初始化有關,不一定是表中的資料,通常情況下,這個結構體變數被存盤到Flash中,

/*LCD部分暫存器設定值串列*/

lcd_redu_list_struct const lcd_redu_list_str[]=

{

{SSD1963_Get_Address_Mode,{0x20} ,1}, /*1*/

{SSD1963_Get_Pll_Mn ,{0x3b,0x02,0x04} ,3}, /*2*/

{SSD1963_Get_Pll_Status ,{0x04} ,1}, /*3*/

{SSD1963_Get_Lcd_Mode ,{0x24,0x20,0x01,0xdf,0x01,0x0f,0x00} ,7}, /*4*/

{SSD1963_Get_Hori_Period ,{0x02,0x0c,0x00,0x2a,0x07,0x00,0x00,0x00},8}, /*5*/

{SSD1963_Get_Vert_Period ,{0x01,0x1d,0x00,0x0b,0x09,0x00,0x00} ,7}, /*6*/

{SSD1963_Get_Power_Mode ,{0x1c} ,1}, /*7*/

{SSD1963_Get_Display_Mode,{0x03} ,1}, /*8*/

{SSD1963_Get_Gpio_Conf ,{0x0F,0x01} ,2}, /*9*/

{SSD1963_Get_Lshift_Freq ,{0x00,0xb8} ,2}, /*10*/

};

實作函式如下所示,函式會遍歷結構體變數中的每一個命令,以及每一個命令下的初始值,如果有一個不正確,則跳出回圈,執行重新初始化和恢復措施,這個函式中的MY_DEBUGF宏是我自己的除錯函式,使用串口列印除錯資訊,在接下來的第五部分將詳細敘述,

通過這個函式,我可以長時間監控顯示屏的哪些命令、哪些位容易被干擾,程式里使用了一個被妖魔化的關鍵字:goto,大多數C語言書籍對goto關鍵字談之色變,但你應該有自己的判斷,在函式內部跳出多重回圈,除了goto關鍵字,又有哪種方法能如此簡潔高效!

/**

* lcd 顯示冗余

* 每隔一段時間呼叫該程式一次

*/

void lcd_redu(void)

{

uint8_t tmp[8];

uint32_t i,j;

uint32_t lcd_init_flag;

lcd_init_flag =0;

for(i=0;i<sizeof(lcd_redu_list_str)/sizeof(lcd_redu_list_str[0]);i+)

{

LCD_SendCommand(lcd_redu_list_str[i].lcd_command);

uyDelay(10);

for(j=0;j<lcd_redu_list_str[i].lcd_value_num;j++)

{

tmp[j]=LCD_ReadData();

if(tmp[j]!=lcd_redu_list_str[i].lcd_get_value[j])

{

lcd_init_flag=0x55;

MY_DEBUGF(MENU_DEBUG,("讀lcd暫存器值與預期不符,命令為:0x%x,第%d個引數,

該引數正確值為:0x%x,實際讀出值為:0x%x\n",lcd_redu_list_str[i].lcd_command,j+1,

lcd_redu_list_str[i].lcd_get_value[j],tmp[j]));

goto handle_lcd_init;

}

}

}

handle_lcd_init:

if(lcd_init_flag==0x55)

{

//重新初始化LCD

//一些必要的恢復措施

}

}

14. 陷阱

對于8051內核單片機,由于沒有相應的硬體支持,可以用純軟體設定軟體陷阱,用來攔截一些程式跑飛,對于ARM7或者Cortex-M系列單片機,硬體已經內建了多種例外,軟體需要根據硬體例外來撰寫陷阱程式,用來快速定位甚至恢復錯誤,

15. 阻塞處理

有時候程式員會使用while(!flag);陳述句阻塞在此等待標志flag改變,比如串口發送時用來等待一位元組資料發送完成,這樣的代碼時存在風險的,如果因為某些原因標志位一直不改變則會造成系統死機,

一個良好冗余的程式是設定一個超時定時器,超過一定時間后,強制程式退出while回圈,

2003年8月11日發生的W32.Blaster.Worm蠕蟲事件導致全球經濟損失高達5億美元,這個漏洞是利用了Windows分布式組件物件模型的遠程程序呼叫介面中的一個邏輯缺陷:在呼叫GetMachineName()函式時,回圈只設定了一個不充分的結束條件,

原代碼簡化如下所示:

微軟發布的安全補丁MS03-026解決了這個問題,為GetMachineName()函式設定了充分終止條件,一個解決代碼簡化如下所示(并非微軟補丁代碼):

HRESULT GetMachineName( WCHAR *pwszPath,

WCHARwszMachineName[MAX_COMPUTTERNAME_LENGTH_FQDN+1])

{

WCHAR *pwszServerName = wszMachineName;

WCHAR *pwszTemp = pwszPath + 2;

WCHAR *end_addr = pwszServerName +MAX_COMPUTTERNAME_LENGTH_FQDN;

while ((*pwszTemp != L’\\’ ) && (*pwszTemp != L’\0’)&& (pwszServerName<end_addr)) /*充分終止條件*/

*pwszServerName++= *pwszTemp++;

/*… */

}

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/293777.html

標籤:其他

上一篇:【智力題】有 1000 瓶藥物,但是其中有一瓶是有毒的,小白鼠吃了一個星期以后就會死掉!請問,在一個星期內找出有毒的 藥物,最少需要多少只小白鼠?

下一篇:畫圖詳解二分查找演算法---C語言實作

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:20:47 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:20:25 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:20:17 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:20:10 more
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:19:44 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:19:07 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:18:57 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:18:49 more
  • 05單件模式

    #經典的單件模式 public class Singleton { private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實體。 // 其他有用的實體變數寫在這里 //構造器宣告為私有,只有Singleton可以實體化這個類! ......

    uj5u.com 2023-04-19 08:42:51 more
  • 【架構與設計】常見微服務分層架構的區別和落地實踐

    軟體工程的方方面面都遵循一個最基本的道理:沒有銀彈,架構分層模型更是如此,每一種都有各自優缺點,所以請根據不同的業務場景,并遵循簡單、可演進這兩個重要的架構原則選擇合適的架構分層模型即可。 ......

    uj5u.com 2023-04-19 08:42:41 more