C++安全編碼:函數

函數

數組作爲函數參數時,必須同時將其長度作爲函數的參數

由於C語言風格的數組,默認沒有結束符,當讀取數組時需要自己根據數組長度進行判斷。(這個也是C/C++)比其他語言運行效率高的一點原因。

嚴禁對公共接口API函數的參數進行ASSERT操作

對於設計成API的函數,必須對參數進行合法性判斷,嚴禁在API實現過程中產生CRASH。
ASSERT,第一會產生crash,release版本又無效;所以assert就顯得沒有意義。

不對內容進行修改的指針型參數,定義爲const

如果參數是指針型參數,且內容不會被修改,請定義爲const類型。

謹慎使用不可重入函數

不可重入函數在多線程環境下其執行結果不能達到預期效果,需謹慎使用。常見的不可重入函數包括:
rand, srand
getenv, getenv_s
strtok
strerror
asctime, ctime, localtime, gmtime
setlocale
atomic_init
tmpnam
mbrtoc16, c16rtomb, mbrtoc32, c32rtomb
gethostbyaddr
gethostbyname
inet_ntoa

可重入概念(簡而言之是可以重複進入,異步信號安全):若一個程序或子程序可以“在任意時刻被中斷然後操作系統調度執行另外一段代碼,這段代碼又調用了該子程序不會出錯”,則稱其爲可重入(reentrant或re-entrant)的。即當該子程序正在運行時,執行線程可以再次進入並執行它,仍然獲得符合設計時預期的結果。與多線程併發執行的線程安全不同,可重入強調對單個線程執行時重新進入同一個子程序仍然是安全的。
一個函數是可重入的,則該函數應當滿足下述條件:

  • 不能含有靜態(全局)非常量數據。
  • 不能返回靜態(全局)非常量數據的地址。
  • 只能處理由調用者提供的數據。
  • 不能依賴於單實例模式資源的鎖。
  • 調用(call)的函數也必需是可重入的。

可重入維基百科
可重入與線程安全:
可重入與線程安全兩個概念都關係到函數處理資源的方式。但是,他們有重大區別

可重入概念會影響函數的外部接口,而線程安全只關心函數的實現。
大多數情況下,要將不可重入函數改爲可重入的,需要修改函數接口,使得所有的數據都通過函數的調用者提供。
要將非線程安全的函數改爲線程安全的,則只需要修改函數的實現部分。一般通過加入同步機制以保護共享的資源,使之不會被幾個線程同時訪問。
操作系統背景與CPU調度策略:
可重入是在單線程操作系統背景下,重入的函數或者子程序,按照後進先出的線性序依次執行完畢。
多線程執行的函數或子程序,各個線程的執行時機是由操作系統調度,不可預期的,但是該函數的每個執行線程都會不時的獲得CPU的時間片,不斷向前推進執行進度。
可重入函數未必是線程安全的;線程安全函數未必是可重入的。
例如,一個函數打開某個文件並讀入數據。這個函數是可重入的,因爲它的多個實例同時執行不會造成衝突;但它不是線程安全的,因爲在它讀入文件時可能有別的線程正在修改該文件,爲了線程安全必須對文件加“同步鎖”。(存疑!!!)
另一個例子,函數在它的函數體內部訪問共享資源使用了加鎖、解鎖操作,所以它是線程安全的,但是卻不可重入。因爲若該函數一個實例運行到已經執行加鎖但未執行解鎖時被停下來,系統又啓動該函數的另外一個實例,則新的實例在加鎖處將轉入等待。如果該函數是一箇中斷處理服務,在中斷處理時又發生新的中斷將導致資源死鎖。fprintf函數就是線程安全但不可重入。
下述例子,是線程安全的,但不是可重入的。

int function()
{
 mutex_lock();
 ...
 function body
 ...
 mutex_unlock();
}

多線程執行時,獲得了互斥鎖的線程總能獲得CPU時間片,向前推進執行進度,最終解開互斥鎖,使得別的線程也能獲得互斥鎖進入臨界區。但是,如果在單線程背景下第一次執行該函數時已經獲得互斥鎖進入臨界區,這時該函數被重入執行,這將在重新申請互斥鎖時被餓死(starvation),因爲獲得了互斥鎖的該函數的第一次執行將永遠沒有機會再獲得CPU時間片。

字符串或指針作爲函數參數時,請檢查參數是否爲NULL

如果字符串或者指針作爲函數參數,爲了防止空指針引用錯誤,在引用前必須確保該參數不爲NULL,如果上層調
用者已經保證了該參數不可能爲NULL,在調用本函數時,在函數開始處可以加ASSERT進行校驗。 例如下面的代
碼,因爲BYTE *p有可能爲NULL,因此在使用前需要進行判斷。

在函數的開始處對參數進行ASSERT操作(API除外)

ASSERT用於檢測代碼設計上的錯誤,如果ASSERT被觸發,說明代碼的設計不符合編碼人員的預期。 在函數的開始
處,對參數進行必要的ASSERT操作,可以在測試階段有效地檢驗編碼人員對代碼設計上的預期。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章