本節書摘來自華章計算機《編寫高品質代碼:改善c程式代碼的125個建議》一書中的第1章,建議2-3,作者:馬 偉 更多章節内容可以通路雲栖社群“華章計算機”公衆号檢視。
c語言标準規定size_t是一種無符号整數類型,編譯器可以根據作業系統的不同而用typedef來定義不同的size_t類型,即在不同的作業系統上所定義的size_t可能不一樣。例如在32位作業系統上可以将size_t定義為unsigned int類型,而在64位作業系統上則可以定義為unsigned long int類型,甚至還可以将size_t定義為unsigned long long int類型等,如下面的示例所示。
在gcc的stddef.h檔案中将size_t定義為:
從上面的定義可以看出,size_t類型的引入增強了程式在不同平台上的可移植性,而它也正是為了友善系統之間的移植而定義的。size_t類型的變量大小足以保證存儲記憶體中對象的大小,任何表示對象長度的變量,包括作為大小、索引、循環計數和長度的整數值,都可以聲明為size_t類型。比如我們常用的sizeof操作符的結果傳回的就是size_t類型,該類型保證能容納實作所建立的最大對象的位元組大小。size_t類型的限制是由size_max宏指定的。
接下來看看size_t類型的使用示例,如代碼清單1-3所示。
不難發現,代碼清單1-3中存在着一個嚴重的問題:當p所引用的動态配置設定的緩沖區在n > int_max時将會發生溢出。我們知道,int 類型的限制是由int_max宏指定的,而size_t類型代表的是一個無符号整數類型,它可能包含一個大于int_max的值。是以,當n的值為0 < n <= int_max時,執行循環n次,代碼如預期一樣正常運作;但當n的值為int_max < n <= size_max,且整型變量i的增值超過int_max時,i的值将是從int_min開始的負值。這時,p[i]所引用的記憶體位置是在p所引用的記憶體之前,這就會導緻寫入發生在數組邊界之外。
是以,為了避免發生這種潛在性的錯誤,應該将變量i也聲明成size_t類型,如代碼清單1-4所示。
除了size_t類型之外,iso/iec tr 24731-1:2007中引入了一種新類型rsize_t,雖然它被定義為size_t類型,但它明确地表示是用于儲存單個對象的長度的。
在vc++ 2010的crtdefs.h檔案中将rsize_t定義為:
在支援rsize_t類型的代碼中,你可以檢查對象的長度,驗證它不大于rsize_max(一個正常單個對象的最大長度),庫函數也可以使用rsize_t進行輸入校驗。
在vc++ 2010的limits.h檔案中将rsize_max定義為:
這樣就消除了示例整數溢出的可能性,現在我們可以将代碼清單1-3中的變量i聲明成rsize _t類型,同時也可将參數n修改成rsize _t類型,并與rsize_max進行比較以驗證資料的合法範圍,如代碼清單1-5所示。