- 編寫高質量代碼:改善C程序代碼的125個建議
- 馬偉 著
- 711字
- 2019-01-01 01:33:08
建議2-3:使用rsize_t或size_t類型來表示一個對象所占用空間的整數值單位
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定義為:
#ifndef __SIZE_TYPE__ #define __SIZE_TYPE__ long unsigned int #endif #if !(defined (__GNUG__) && defined (size_t)) typedef __SIZE_TYPE__ size_t; #ifdef __BEOS__ typedef long ssize_t; #endif /* __BEOS__ */
而在VC++2010的crtdefs.h文件中將size_t定義為:
#ifndef _SIZE_T_DEFINED #ifdef _WIN64 typedef unsigned __int64 size_t; #else typedef _W64 unsigned int size_t; #endif #define _SIZE_T_DEFINED #endif
從上面的定義可以看出,size_t類型的引入增強了程序在不同平臺上的可移植性,而它也正是為了方便系統之間的移植而定義的。size_t類型的變量大小足以保證存儲內存中對象的大小,任何表示對象長度的變量,包括作為大小、索引、循環計數和長度的整數值,都可以聲明為size_t類型。比如我們常用的sizeof操作符的結果返回的就是size_t類型,該類型保證能容納實現所建立的最大對象的字節大小。size_t類型的限制是由SIZE_MAX宏指定的。
接下來看看size_t類型的使用示例,如代碼清單1-3所示。
代碼清單1-3 size_t類型的使用示例
char *copy(size_t n, const char *str) { int i; char *p; if (n == 0) { /* 處理n==0的情況 */ } p = (char *)malloc(n); if (p == NULL) { /*處理p==NULL的情況 */ } for ( i = 0; i < n; ++i ) { p[i] = *str++; } return p; }
不難發現,代碼清單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所示。
代碼清單1-4 代碼清單1-3的解決方法
char *copy(size_t n, const char *str) { size_t i; char *p; if (n == 0||n>SIZE_MAX) { /* 處理n==0的情況 */ } p = (char *)malloc(n); if (p == NULL) { /*處理p==NULL的情況 */ } for ( i = 0; i < n; ++i ) { p[i] = *str++; } return p; }
除了size_t類型之外,ISO/IEC TR 24731-1:2007中引入了一種新類型rsize_t,雖然它被定義為size_t類型,但它明確地表示是用于保存單個對象的長度的。
在VC++2010的crtdefs.h文件中將rsize_t定義為:
#if __STDC_WANT_SECURE_LIB__ #ifndef _RSIZE_T_DEFINED typedef size_t rsize_t; #define _RSIZE_T_DEFINED #endif #endif
在支持rsize_t類型的代碼中,你可以檢查對象的長度,驗證它不大于RSIZE_MAX(一個正常單個對象的最大長度),庫函數也可以使用rsize_t進行輸入校驗。
在VC++2010的limits.h文件中將RSIZE_MAX定義為:
#if __STDC_WANT_SECURE_LIB__ #ifndef RSIZE_MAX #define RSIZE_MAX SIZE_MAX #endif #endif
這樣就消除了示例整數溢出的可能性,現在我們可以將代碼清單1-3中的變量i聲明成rsize_t類型,同時也可將參數n修改成rsize_t類型,并與RSIZE_MAX進行比較以驗證數據的合法范圍,如代碼清單1-5所示。
代碼清單1-5 代碼清單1-3的rsize_t解決方法
char *copy(rsize_t n, const char *str) { rsize_t i; char *p; if (n == 0 || n > RSIZE_MAX) { /* 處理n==0|| n > RSIZE_MAX的情況 */ } p = (char *)malloc(n); if (p == NULL) { /*處理p==NULL的情況 */ } for (i = 0; i < n; ++i) { p[i] = *str++; } return p; }
- 小程序實戰視頻課:微信小程序開發全案精講
- Learning Selenium Testing Tools with Python
- AngularJS Web Application Development Blueprints
- Python從入門到精通(精粹版)
- C#程序設計(慕課版)
- 青少年美育趣味課堂:XMind思維導圖制作
- Mastering JBoss Enterprise Application Platform 7
- C程序設計實踐教程
- Mastering Linux Security and Hardening
- Extending Unity with Editor Scripting
- Java程序設計基礎(第6版)
- C語言程序設計與應用實驗指導書(第2版)
- Node.js實戰:分布式系統中的后端服務開發
- JavaScript Mobile Application Development
- Getting Started with the Lazarus IDE